Signed-off-by: Randy Li <ay...@soulik.info>
---
 drivers/staging/rockchip-mpp/Kconfig          |  52 +
 drivers/staging/rockchip-mpp/Makefile         |  16 +
 drivers/staging/rockchip-mpp/mpp_debug.h      |  87 ++
 drivers/staging/rockchip-mpp/mpp_dev_common.c | 971 ++++++++++++++++++
 drivers/staging/rockchip-mpp/mpp_dev_common.h | 219 ++++
 drivers/staging/rockchip-mpp/mpp_dev_rkvdec.c | 856 +++++++++++++++
 drivers/staging/rockchip-mpp/mpp_dev_vdpu1.c  | 615 +++++++++++
 drivers/staging/rockchip-mpp/mpp_dev_vdpu2.c  | 577 +++++++++++
 drivers/staging/rockchip-mpp/mpp_dev_vepu1.c  | 481 +++++++++
 drivers/staging/rockchip-mpp/mpp_dev_vepu2.c  | 478 +++++++++
 drivers/staging/rockchip-mpp/mpp_iommu_dma.c  | 292 ++++++
 drivers/staging/rockchip-mpp/mpp_iommu_dma.h  |  42 +
 drivers/staging/rockchip-mpp/mpp_service.c    | 197 ++++
 drivers/staging/rockchip-mpp/mpp_service.h    |  38 +
 include/uapi/video/rk_vpu_service.h           | 101 ++
 15 files changed, 5022 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_vdpu1.c
 create mode 100644 drivers/staging/rockchip-mpp/mpp_dev_vdpu2.c
 create mode 100644 drivers/staging/rockchip-mpp/mpp_dev_vepu1.c
 create mode 100644 drivers/staging/rockchip-mpp/mpp_dev_vepu2.c
 create mode 100644 drivers/staging/rockchip-mpp/mpp_iommu_dma.c
 create mode 100644 drivers/staging/rockchip-mpp/mpp_iommu_dma.h
 create mode 100644 drivers/staging/rockchip-mpp/mpp_service.c
 create mode 100644 drivers/staging/rockchip-mpp/mpp_service.h
 create mode 100644 include/uapi/video/rk_vpu_service.h

diff --git a/drivers/staging/rockchip-mpp/Kconfig 
b/drivers/staging/rockchip-mpp/Kconfig
new file mode 100644
index 000000000000..691ddc3bcd14
--- /dev/null
+++ b/drivers/staging/rockchip-mpp/Kconfig
@@ -0,0 +1,52 @@
+# 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
+       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_VDPU1_DEVICE
+       tristate "VPU decoder v1 device driver"
+       depends on ROCKCHIP_MPP_DEVICE
+       default n
+       help
+         rockchip mpp vpu decoder v1.
+
+config ROCKCHIP_MPP_VEPU1_DEVICE
+       tristate "VPU encoder v1 device driver"
+       depends on ROCKCHIP_MPP_DEVICE
+       default n
+       help
+         rockchip mpp vpu encoder v1.
+
+config ROCKCHIP_MPP_VDPU2_DEVICE
+       tristate "VPU decoder v2 device driver"
+       depends on ROCKCHIP_MPP_DEVICE
+       default n
+       help
+         rockchip mpp vpu decoder v2.
+
+config ROCKCHIP_MPP_VEPU2_DEVICE
+       tristate "VPU encoder v2 device driver"
+       depends on ROCKCHIP_MPP_DEVICE
+       default n
+       help
+         rockchip mpp vpu encoder v2.
+endmenu
diff --git a/drivers/staging/rockchip-mpp/Makefile 
b/drivers/staging/rockchip-mpp/Makefile
new file mode 100644
index 000000000000..06a9c58c92cb
--- /dev/null
+++ b/drivers/staging/rockchip-mpp/Makefile
@@ -0,0 +1,16 @@
+# SPDX-License-Identifier: GPL-2.0
+rk-mpp-service-objs := mpp_service.o
+rk-mpp-device-objs := mpp_dev_common.o mpp_iommu_dma.o
+rk-mpp-vdec-objs := mpp_dev_rkvdec.o
+rk-mpp-vdpu1-objs := mpp_dev_vdpu1.o
+rk-mpp-vdpu2-objs := mpp_dev_vdpu2.o
+rk-mpp-vepu1-objs := mpp_dev_vepu1.o
+rk-mpp-vepu2-objs := mpp_dev_vepu2.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_VDPU1_DEVICE) += rk-mpp-vdpu1.o
+obj-$(CONFIG_ROCKCHIP_MPP_VEPU1_DEVICE) += rk-mpp-vepu1.o
+obj-$(CONFIG_ROCKCHIP_MPP_VDPU2_DEVICE) += rk-mpp-vdpu2.o
+obj-$(CONFIG_ROCKCHIP_MPP_VEPU2_DEVICE) += rk-mpp-vepu2.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..159aa5d244ce
--- /dev/null
+++ b/drivers/staging/rockchip-mpp/mpp_dev_common.c
@@ -0,0 +1,971 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2016 - 2017 Fuzhou Rockchip Electronics Co., Ltd
+ *             Randy Li, <ay...@soulik.info>
+ *
+ * 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/module.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/of_irq.h>
+#include <linux/pm_runtime.h>
+#include <linux/poll.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <video/rk_vpu_service.h>
+
+#include "mpp_debug.h"
+#include "mpp_dev_common.h"
+#include "mpp_iommu_dma.h"
+#include "mpp_service.h"
+
+#define MPP_TIMEOUT_DELAY              (2000)
+
+#define MPP_SESSION_MAX_DONE_TASK      (20)
+
+#ifdef CONFIG_COMPAT
+struct compat_mpp_request {
+       compat_uptr_t req;
+       u32 size;
+};
+#endif
+
+static struct class *mpp_device_class;
+
+static int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "bit switch for mpp device debug information");
+
+static void *mpp_fd_to_mem_region(struct rockchip_mpp_dev *mpp_dev,
+                                 struct mpp_dma_session *dma, int fd)
+{
+       struct mpp_mem_region *mem_region = NULL;
+       dma_addr_t iova;
+
+       if (fd <= 0 || !dma || !mpp_dev)
+               return ERR_PTR(-EINVAL);
+
+       read_lock(&mpp_dev->resource_rwlock);
+       iova = mpp_dma_import_fd(dma, fd);
+       read_unlock(&mpp_dev->resource_rwlock);
+       if (IS_ERR_VALUE(iova)) {
+               mpp_err("can't access dma-buf %d\n", fd);
+               return ERR_PTR(-EINVAL);
+       }
+
+       mem_region = kzalloc(sizeof(*mem_region), GFP_KERNEL);
+       if (!mem_region) {
+               read_lock(&mpp_dev->resource_rwlock);
+               mpp_dma_release_fd(dma, fd);
+               read_unlock(&mpp_dev->resource_rwlock);
+               return ERR_PTR(-ENOMEM);
+       }
+
+       mem_region->hdl = (void *)(long)fd;
+       mem_region->iova = iova;
+
+       return mem_region;
+}
+
+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);
+       wake_up(&session->wait);
+}
+
+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;
+
+       mpp_debug_time_diff(task);
+
+       if (mpp_dev->ops->finish)
+               mpp_dev->ops->finish(mpp_dev, task);
+
+       atomic_dec(&task->session->task_running);
+       /*
+        * 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;
+
+       if (mpp_dev->ops->free_task)
+               mpp_dev->ops->free_task(session, task);
+       return 0;
+}
+
+struct mpp_mem_region *mpp_dev_task_attach_fd(struct mpp_task *task, int fd)
+{
+       struct mpp_mem_region *mem_region = NULL;
+
+       mem_region = mpp_fd_to_mem_region(task->session->mpp,
+                                         task->session->dma, fd);
+       if (IS_ERR(mem_region))
+               return mem_region;
+
+       INIT_LIST_HEAD(&mem_region->reg_lnk);
+       list_add_tail(&mem_region->reg_lnk, &task->mem_region_list);
+
+       return mem_region;
+}
+EXPORT_SYMBOL(mpp_dev_task_attach_fd);
+
+int mpp_reg_address_translate(struct rockchip_mpp_dev *mpp,
+                             struct mpp_task *task, int fmt, u32 *reg)
+{
+       struct mpp_trans_info *trans_info = mpp->variant->trans_info;
+       const u8 *tbl = trans_info[fmt].table;
+       int size = trans_info[fmt].count;
+       int i;
+
+       mpp_debug_enter();
+       for (i = 0; i < size; i++) {
+               struct mpp_mem_region *mem_region = NULL;
+               int usr_fd = reg[tbl[i]] & 0x3FF;
+               int offset = reg[tbl[i]] >> 10;
+
+               if (usr_fd == 0)
+                       continue;
+
+               mem_region = mpp_dev_task_attach_fd(task, usr_fd);
+               if (IS_ERR(mem_region)) {
+                       mpp_debug(DEBUG_IOMMU, "reg[%3d]: %08x failed\n",
+                                 tbl[i], reg[tbl[i]]);
+                       return PTR_ERR(mem_region);
+               }
+
+               mem_region->reg_idx = tbl[i];
+               mpp_debug(DEBUG_IOMMU, "reg[%3d]: %3d => %pad + offset %10d\n",
+                         tbl[i], usr_fd, &mem_region->iova, offset);
+               reg[tbl[i]] = mem_region->iova + offset;
+       }
+
+       mpp_debug_leave();
+
+       return 0;
+}
+EXPORT_SYMBOL(mpp_reg_address_translate);
+
+void mpp_translate_extra_info(struct mpp_task *task,
+                             struct extra_info_for_iommu *ext_inf,
+                             u32 *reg)
+{
+       mpp_debug_enter();
+       if (ext_inf) {
+               int i;
+
+               if (ext_inf->magic != EXTRA_INFO_MAGIC)
+                       return;
+
+               for (i = 0; i < ext_inf->cnt; i++) {
+                       mpp_debug(DEBUG_IOMMU, "reg[%d] + offset %d\n",
+                                 ext_inf->elem[i].index,
+                                 ext_inf->elem[i].offset);
+                       reg[ext_inf->elem[i].index] += ext_inf->elem[i].offset;
+               }
+       }
+       mpp_debug_leave();
+}
+EXPORT_SYMBOL(mpp_translate_extra_info);
+
+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_LIST_HEAD(&task->mem_region_list);
+       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;
+       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)
+{
+       struct rockchip_mpp_dev *mpp_dev = NULL;
+       struct mpp_mem_region *mem_region = NULL, *n;
+
+       mpp_dev = session->mpp;
+       /* release memory region attach to this registers table. */
+       list_for_each_entry_safe(mem_region, n,
+                                &task->mem_region_list, reg_lnk) {
+               read_lock(&mpp_dev->resource_rwlock);
+               mpp_dma_release_fd(session->dma, (long)mem_region->hdl);
+               read_unlock(&mpp_dev->resource_rwlock);
+               list_del_init(&mem_region->reg_lnk);
+               kfree(mem_region);
+       }
+}
+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);
+}
+
+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);
+
+       mpp_iommu_detach(mpp_dev->iommu_info);
+       mpp_dev->ops->reset(mpp_dev);
+       mpp_iommu_attach(mpp_dev->iommu_info);
+
+       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);
+       /*
+        * In the link table mode, the prepare function of the device
+        * will check whether I can insert a new task into device.
+        * If the device supports the task status query(like the HEVC
+        * encoder), it can report whether the device is busy.
+        * If the device doesn't support multiple task or task status
+        * query, leave this job to mpp service.
+        */
+       if (mpp_dev->ops->prepare)
+               ret = mpp_dev->ops->prepare(mpp_dev, task);
+       if (ret == -EINVAL)
+               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, u32 __user *dst, u32 size)
+{
+       mpp_debug_enter();
+
+       if (!mpp_dev || !task)
+               return -EINVAL;
+
+       if (mpp_dev->ops->result)
+               mpp_dev->ops->result(mpp_dev, task, dst, size);
+
+       mpp_dev_free_task(task->session, task);
+
+       mpp_debug_leave();
+       return 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;
+
+       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;
+}
+
+#ifdef CONFIG_COMPAT
+
+#define VPU_IOC_SET_CLIENT_TYPE32          _IOW(VPU_IOC_MAGIC, 1, u32)
+#define VPU_IOC_GET_HW_FUSE_STATUS32       _IOW(VPU_IOC_MAGIC, 2, \
+                                               compat_ulong_t)
+#define VPU_IOC_SET_REG32                  _IOW(VPU_IOC_MAGIC, 3, \
+                                               compat_ulong_t)
+#define VPU_IOC_GET_REG32                  _IOW(VPU_IOC_MAGIC, 4, \
+                                               compat_ulong_t)
+#define VPU_IOC_PROBE_IOMMU_STATUS32       _IOR(VPU_IOC_MAGIC, 5, u32)
+
+static long native_ioctl(struct file *file, unsigned int cmd, unsigned long 
arg)
+{
+       long ret = -ENOIOCTLCMD;
+
+       if (file->f_op->unlocked_ioctl)
+               ret = file->f_op->unlocked_ioctl(file, cmd, arg);
+
+       return ret;
+}
+
+long mpp_dev_compat_ioctl(struct file *file, unsigned int cmd,
+                         unsigned long arg)
+{
+       struct vpu_request req;
+       void __user *up = compat_ptr(arg);
+       int compatible_arg = 1;
+       long err = 0;
+
+       mpp_debug_enter();
+       mpp_debug(DEBUG_IOCTL, "cmd %x, VPU_IOC_SET_CLIENT_TYPE32 %x\n", cmd,
+                 (u32)VPU_IOC_SET_CLIENT_TYPE32);
+       /* First, convert the command. */
+       switch (cmd) {
+       case VPU_IOC_SET_CLIENT_TYPE32:
+               cmd = VPU_IOC_SET_CLIENT_TYPE;
+               break;
+       case VPU_IOC_GET_HW_FUSE_STATUS32:
+               cmd = VPU_IOC_GET_HW_FUSE_STATUS;
+               break;
+       case VPU_IOC_SET_REG32:
+               cmd = VPU_IOC_SET_REG;
+               break;
+       case VPU_IOC_GET_REG32:
+               cmd = VPU_IOC_GET_REG;
+               break;
+       case VPU_IOC_PROBE_IOMMU_STATUS32:
+               cmd = VPU_IOC_PROBE_IOMMU_STATUS;
+               break;
+       }
+       switch (cmd) {
+       case VPU_IOC_SET_REG:
+       case VPU_IOC_GET_REG:
+       case VPU_IOC_GET_HW_FUSE_STATUS: {
+               compat_uptr_t req_ptr;
+               struct compat_mpp_request __user *req32 = NULL;
+
+               req32 = (struct compat_mpp_request __user *)up;
+               memset(&req, 0, sizeof(req));
+
+               if (get_user(req_ptr, &req32->req) ||
+                   get_user(req.size, &req32->size)) {
+                       mpp_err("error: compat get hw status copy_from_user 
failed\n");
+                       return -EFAULT;
+               }
+               req.req = compat_ptr(req_ptr);
+               compatible_arg = 0;
+       } break;
+       }
+
+       if (compatible_arg) {
+               err = native_ioctl(file, cmd, (unsigned long)up);
+       } else {
+               mm_segment_t old_fs = get_fs();
+
+               set_fs(KERNEL_DS);
+               err = native_ioctl(file, cmd, (unsigned long)&req);
+               set_fs(old_fs);
+       }
+
+       mpp_debug_leave();
+       return err;
+}
+EXPORT_SYMBOL(mpp_dev_compat_ioctl);
+#endif
+
+static int mpp_dev_open(struct inode *inode, struct file *filp)
+{
+       struct rockchip_mpp_dev *mpp = container_of(inode->i_cdev,
+                                                   struct rockchip_mpp_dev,
+                                                   mpp_cdev);
+       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 = mpp;
+       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 -ENOMEM;
+       }
+
+       atomic_set(&session->task_running, 0);
+       session->dma = mpp_dma_session_create(mpp->dev);
+       INIT_LIST_HEAD(&session->list_session);
+       filp->private_data = (void *)session;
+
+       mpp_dev_power_on(mpp);
+       mpp_debug_leave();
+
+       return nonseekable_open(inode, filp);
+}
+
+static int mpp_dev_release(struct inode *inode, struct file *filp)
+{
+       struct rockchip_mpp_dev *mpp = container_of(inode->i_cdev,
+                                                   struct rockchip_mpp_dev,
+                                                   mpp_cdev);
+       int task_running;
+       struct mpp_session *session = filp->private_data;
+
+       mpp_debug_enter();
+       if (!session)
+               return -EINVAL;
+
+       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);
+
+       /* remove this filp from the asynchronusly notified filp's */
+       mpp_dev_session_clear(mpp, session);
+
+       read_lock(&session->mpp->resource_rwlock);
+       mpp_dma_destroy_session(session->dma);
+       read_unlock(&session->mpp->resource_rwlock);
+       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 file_operations mpp_dev_default_fops = {
+       .unlocked_ioctl = mpp_dev_ioctl,
+       .open           = mpp_dev_open,
+       .release        = mpp_dev_release,
+       .poll           = mpp_dev_poll,
+#ifdef CONFIG_COMPAT
+       .compat_ioctl   = mpp_dev_compat_ioctl,
+#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;
+}
+
+/* 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_enable(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;
+       }
+
+       pm_runtime_get_sync(dev);
+       /*
+        * TODO: here or at the device itself, some device doesn't
+        * have the iommu, maybe in the device is better.
+        */
+       mpp_dev->iommu_info = mpp_iommu_probe(dev);
+       if (IS_ERR(mpp_dev->iommu_info)) {
+               dev_err(dev, "failed to attach iommu: %ld\n",
+                       PTR_ERR(mpp_dev->iommu_info));
+       }
+
+       pm_runtime_put(dev);
+
+       return 0;
+
+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 *fops)
+{
+       struct device *dev = mpp_dev->dev;
+       int ret = 0;
+
+       /* create a device node */
+       ret = alloc_chrdev_region(&mpp_dev->dev_id, 0, 1, node_name);
+       if (ret) {
+               dev_err(dev, "alloc dev_t failed\n");
+               return ret;
+       }
+
+       if (fops)
+               cdev_init(&mpp_dev->mpp_cdev, fops);
+       else
+               cdev_init(&mpp_dev->mpp_cdev, &mpp_dev_default_fops);
+       mpp_dev->mpp_cdev.owner = THIS_MODULE;
+
+       ret = cdev_add(&mpp_dev->mpp_cdev, mpp_dev->dev_id, 1);
+       if (ret) {
+               unregister_chrdev_region(mpp_dev->dev_id, 1);
+               dev_err(dev, "add device node failed\n");
+               return ret;
+       }
+
+       device_create(mpp_device_class, dev, mpp_dev->dev_id, NULL, "%s",
+                     node_name);
+
+       return 0;
+}
+EXPORT_SYMBOL(mpp_dev_register_node);
+
+int mpp_dev_common_remove(struct rockchip_mpp_dev *mpp_dev)
+{
+       destroy_workqueue(mpp_dev->irq_workq);
+
+       device_destroy(mpp_device_class, mpp_dev->dev_id);
+       cdev_del(&mpp_dev->mpp_cdev);
+       unregister_chrdev_region(mpp_dev->dev_id, 1);
+
+       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)
+               do_gettimeofday(&task->start);
+}
+EXPORT_SYMBOL(mpp_debug_time_record);
+
+void mpp_debug_time_diff(struct mpp_task *task)
+{
+       struct timeval end;
+
+       do_gettimeofday(&end);
+       mpp_debug(DEBUG_TIMING, "time: %ld us\n",
+                 (end.tv_sec  - task->start.tv_sec)  * 1000000 +
+                 (end.tv_usec - task->start.tv_usec));
+}
+EXPORT_SYMBOL(mpp_debug_time_diff);
+
+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..8a7dc7444dc3
--- /dev/null
+++ b/drivers/staging/rockchip-mpp/mpp_dev_common.h
@@ -0,0 +1,219 @@
+/* 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/cdev.h>
+#include <linux/dma-buf.h>
+#include <linux/kfifo.h>
+#include <linux/types.h>
+#include <linux/workqueue.h>
+#include <linux/reset.h>
+
+#include "mpp_service.h"
+
+#define MPP_IOC_CUSTOM_BASE                    0x1000
+
+#define EXTRA_INFO_MAGIC                       (0x4C4A46)
+#define JPEG_IOC_EXTRA_SIZE                    (48)
+
+struct mpp_trans_info {
+       const int count;
+       const char * const table;
+};
+
+struct extra_info_elem {
+       u32 index;
+       u32 offset;
+};
+
+struct extra_info_for_iommu {
+       u32 magic;
+       u32 cnt;
+       struct extra_info_elem elem[20];
+};
+
+struct mpp_dev_variant {
+       u32 reg_len;
+       struct mpp_trans_info *trans_info;
+       const char *node_name;
+       u32 version_bit;
+};
+
+struct mpp_mem_region {
+       struct list_head srv_lnk;
+       struct list_head reg_lnk;
+       struct list_head session_lnk;
+       /* virtual address for iommu */
+       dma_addr_t iova;
+       unsigned long len;
+       u32 reg_idx;
+       void *hdl;
+};
+
+/* Definition in dma file */
+struct mpp_dma_session;
+/* 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;
+
+       void __iomem *reg_base;
+       int irq;
+       struct workqueue_struct *irq_workq;
+
+       struct mpp_iommu_info *iommu_info;
+       rwlock_t resource_rwlock;
+       atomic_t reset_request;
+
+       struct cdev mpp_cdev;
+       dev_t dev_id;
+
+       /* MPP Service */
+       struct mpp_service_node *srv;
+};
+
+struct mpp_task;
+
+struct mpp_session {
+       /* the session related device private data */
+       struct rockchip_mpp_dev *mpp;
+       /* a linked list of data so we can access them for debugging */
+       struct list_head list_session;
+       struct mpp_dma_session *dma;
+
+       /* 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;
+};
+
+/* 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;
+       /* The DMA buffer used in this task */
+       struct list_head mem_region_list;
+       struct work_struct work;
+
+       /* record context running start time */
+       struct timeval 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 mpp_mem_region *mpp_dev_task_attach_fd(struct mpp_task *task, int fd);
+int mpp_reg_address_translate(struct rockchip_mpp_dev *data,
+                             struct mpp_task *task, int fmt, u32 *reg);
+void mpp_translate_extra_info(struct mpp_task *task,
+                             struct extra_info_for_iommu *ext_inf,
+                             u32 *reg);
+
+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);
+
+/* It can handle the default ioctl */
+long mpp_dev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg);
+#ifdef CONFIG_COMPAT
+long mpp_dev_compat_ioctl(struct file *filp, unsigned int cmd,
+                         unsigned long arg);
+#endif
+
+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 *fops);
+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..a3da27cfc10e
--- /dev/null
+++ b/drivers/staging/rockchip-mpp/mpp_dev_rkvdec.c
@@ -0,0 +1,856 @@
+// 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/rockchip/rockchip_sip.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <soc/rockchip/pm_domains.h>
+
+#include "mpp_debug.h"
+#include "mpp_dev_common.h"
+#include "mpp_iommu_dma.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"
+
+/* 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)
+
+#define MPP_ALIGN_SIZE 0x1000
+
+#define MHZ            (1000 * 1000)
+#define DEF_ACLK       400
+#define DEF_CORE       250
+#define DEF_CABAC      300
+
+#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 strm_base;
+       u32 irq_status;
+};
+
+/*
+ * file handle translate information
+ */
+static const char trans_tbl_h264d[] = {
+       4, 6, 7, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,
+       23, 24, 41, 42, 43, 48, 75
+};
+
+static const char trans_tbl_h265d[] = {
+       4, 6, 7, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,
+       23, 24, 42, 43
+};
+
+static const char trans_tbl_vp9d[] = {
+       4, 6, 7, 11, 12, 13, 14, 15, 16
+};
+
+static struct mpp_trans_info trans_rk_hevcdec[] = {
+       [RKVDEC_FMT_H265D] = {
+               .count = sizeof(trans_tbl_h265d),
+               .table = trans_tbl_h265d,
+       },
+};
+
+static struct mpp_trans_info trans_rkvdec[] = {
+       [RKVDEC_FMT_H265D] = {
+               .count = sizeof(trans_tbl_h265d),
+               .table = trans_tbl_h265d,
+       },
+       [RKVDEC_FMT_H264D] = {
+               .count = sizeof(trans_tbl_h264d),
+               .table = trans_tbl_h264d,
+       },
+       [RKVDEC_FMT_VP9D] = {
+               .count = sizeof(trans_tbl_vp9d),
+               .table = trans_tbl_vp9d,
+       },
+};
+
+static const struct mpp_dev_variant rkvdec_v1_data = {
+       .reg_len = 76,
+       .trans_info = trans_rkvdec,
+       .node_name = RKVDEC_NODE_NAME,
+       .version_bit = BIT(0),
+};
+
+static const struct mpp_dev_variant rkvdec_v1p_data = {
+       .reg_len = 76,
+       .trans_info = trans_rkvdec,
+       .node_name = RKVDEC_NODE_NAME,
+       .version_bit = RKVDEC_VER_RK3328_BIT,
+};
+
+
+static const struct mpp_dev_variant rk_hevcdec_data = {
+       .reg_len = 48,
+       .trans_info = trans_rk_hevcdec,
+       .node_name = RK_HEVCDEC_NODE_NAME,
+       .version_bit = BIT(0),
+};
+
+static void *rockchip_rkvdec_get_drv_data(struct platform_device *pdev);
+
+/*
+ * 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;
+       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, 0, dmabuf->size,
+                                      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, 0, dmabuf->size, DMA_FROM_DEVICE);
+       dma_buf_put(dmabuf);
+
+       return ret;
+}
+
+static void *rockchip_mpp_rkvdec_alloc_task(struct mpp_session *session,
+                                           void __user *src, u32 size)
+{
+       struct rkvdec_task *task = NULL;
+       u32 reg_len;
+       u32 fmt = 0;
+       u32 dwsize = size / sizeof(u32);
+       int pps_fd;
+       u32 pps_offset;
+       int err = -EFAULT;
+
+       mpp_debug_enter();
+
+       task = kzalloc(sizeof(*task), GFP_KERNEL);
+       if (!task)
+               return NULL;
+
+       mpp_dev_task_init(session, &task->mpp_task);
+
+       reg_len = dwsize > ROCKCHIP_RKVDEC_REG_NUM ?
+               ROCKCHIP_RKVDEC_REG_NUM : dwsize;
+
+       if (copy_from_user(task->reg, src, reg_len * 4)) {
+               mpp_err("error: copy_from_user failed in reg_init\n");
+               err = -EFAULT;
+               goto fail;
+       }
+
+       fmt = RKVDEC_GET_FORMAT(task->reg[RKVDEC_REG_SYS_CTRL_INDEX]);
+       /*
+        * special offset scale case
+        *
+        * This translation is for fd + offset translation.
+        * One register has 32bits. We need to transfer both buffer file
+        * handle and the start address offset so we packet file handle
+        * and offset together using below format.
+        *
+        *  0~9  bit for buffer file handle range 0 ~ 1023
+        * 10~31 bit for offset range 0 ~ 4M
+        *
+        * But on 4K case the offset can be larger the 4M
+        * So on VP9 4K decoder colmv base we scale the offset by 16
+        */
+       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;
+       }
+
+       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;
+                       }
+               }
+       }
+
+       err = mpp_reg_address_translate(session->mpp, &task->mpp_task, fmt,
+                                       task->reg);
+       if (err) {
+               mpp_err("error: translate reg address failed.\n");
+
+               if (unlikely(debug & DEBUG_DUMP_ERR_REG))
+                       mpp_debug_dump_reg_mem(task->reg,
+                                              ROCKCHIP_RKVDEC_REG_NUM);
+               goto fail;
+       }
+
+       task->strm_base = task->reg[RKVDEC_REG_STREAM_RLC_BASE_INDEX];
+
+       mpp_debug_leave();
+
+       return &task->mpp_task;
+
+fail:
+       mpp_dev_task_finalize(session, &task->mpp_task);
+       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)
+{
+       struct rkvdec_task *task = to_rkvdec_task(mpp_task);
+
+       /* FIXME may overflow the kernel */
+       if (copy_to_user(dst, task->reg, size)) {
+               mpp_err("copy_to_user failed\n");
+               return -EIO;
+       }
+
+       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;
+
+       /* TODO: use devm_reset_control_get_share() instead */
+       dec_dev->rst_a = devm_reset_control_get(mpp_dev->dev, "video_a");
+       dec_dev->rst_h = devm_reset_control_get(mpp_dev->dev, "video_h");
+       dec_dev->rst_core = devm_reset_control_get(mpp_dev->dev, "video_core");
+       /* 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_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;
+       }
+
+       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;
+}
+
+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;
+}
+
+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,
+};
+
+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,
+};
+
+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 (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);
+       }
+       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;
+
+       ret = mpp_dev_register_node(mpp_dev, mpp_dev->variant->node_name, NULL);
+       if (ret)
+               dev_err(dev, "register char device failed: %d\n", ret);
+
+       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_vdpu1.c 
b/drivers/staging/rockchip-mpp/mpp_dev_vdpu1.c
new file mode 100644
index 000000000000..4371a1a6080b
--- /dev/null
+++ b/drivers/staging/rockchip-mpp/mpp_dev_vdpu1.c
@@ -0,0 +1,615 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd
+ *             Randy Li, <ay...@soulik.info>
+ *
+ * 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 "mpp_debug.h"
+#include "mpp_dev_common.h"
+
+#define RKVDPU1_DRIVER_NAME            "mpp_vdpu1"
+#define RKVDPU1_NODE_NAME              "vpu-service"
+
+#define RKAVSD1_NODE_NAME              "avsd"
+
+/* The maximum registers number of all the version */
+#define ROCKCHIP_VDPU1_REG_NUM                 108
+
+#define RKVDPU1_REG_DEC_INT_EN                 0x004
+#define RKVDPU1_REG_DEC_INT_EN_INDEX           (1)
+/* B slice detected, used in 8190 decoder and later */
+#define                RKVDPU1_INT_PIC_INF             BIT(24)
+#define                RKVDPU1_INT_TIMEOUT             BIT(18)
+#define                RKVDPU1_INT_SLICE               BIT(17)
+#define                RKVDPU1_INT_STRM_ERROR          BIT(16)
+#define                RKVDPU1_INT_ASO_ERROR           BIT(15)
+#define                RKVDPU1_INT_BUF_EMPTY           BIT(14)
+#define                RKVDPU1_INT_BUS_ERROR           BIT(13)
+#define                RKVDPU1_DEC_INT                 BIT(12)
+#define                RKVDPU1_DEC_INT_RAW             BIT(8)
+#define                RKVDPU1_DEC_IRQ_DIS             BIT(4)
+#define                RKVDPU1_DEC_START               BIT(0)
+
+#define RKVDPU1_REG_DEC_DEV_CTRL               0x008
+#define RKVDPU1_REG_DEC_DEV_CTRL_INDEX         (2)
+/* NOTE: Don't enable it or decoding AVC would meet problem at rk3288 */
+#define                RKVDPU1_CLOCK_GATE_EN           BIT(10)
+
+#define RKVDPU1_REG_SYS_CTRL                   0x00c
+#define RKVDPU1_REG_SYS_CTRL_INDEX             (3)
+#define                RKVDPU1_GET_FORMAT(x)           (((x) >> 28) & 0xf)
+#define                RKVDPU1_FMT_H264D               0
+#define                RKVDPU1_FMT_MPEG4D              1
+#define                RKVDPU1_FMT_H263D               2
+#define                RKVDPU1_FMT_JPEGD               3
+#define                RKVDPU1_FMT_VC1D                4
+#define                RKVDPU1_FMT_MPEG2D              5
+#define                RKVDPU1_FMT_MPEG1D              6
+#define                RKVDPU1_FMT_VP6D                7
+#define                RKVDPU1_FMT_RESERVED            8
+#define                RKVDPU1_FMT_VP7D                9
+#define                RKVDPU1_FMT_VP8D                10
+#define                RKVDPU1_FMT_AVSD                11
+
+#define RKVDPU1_REG_STREAM_RLC_BASE            0x030
+#define RKVDPU1_REG_STREAM_RLC_BASE_INDEX      (12)
+
+#define RKVDPU1_REG_DIR_MV_BASE                        0x0a4
+#define RKVDPU1_REG_DIR_MV_BASE_INDEX          (41)
+
+#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 vdpu1 debug information");
+
+struct vdpu1_dev_data {
+       struct mpp_dev_variant dec_data;
+       bool pp_support;
+};
+
+struct rockchip_rkvdpu_dev {
+       struct rockchip_mpp_dev mpp_dev;
+       struct vdpu1_dev_data *data;
+
+       struct reset_control *rst_a;
+       struct reset_control *rst_h;
+
+       void *current_task;
+};
+
+struct rkvdpu_task {
+       struct mpp_task mpp_task;
+
+       u32 reg[ROCKCHIP_VDPU1_REG_NUM];
+       u32 idx;
+       struct extra_info_for_iommu ext_inf;
+
+       u32 strm_base;
+       u32 irq_status;
+};
+
+/*
+ * file handle translate information
+ */
+static const char trans_tbl_avsd[] = {
+       12, 13, 14, 15, 16, 17, 40, 41, 45
+};
+
+static const char trans_tbl_default[] = {
+       12, 13, 14, 15, 16, 17, 40, 41
+};
+
+static const char trans_tbl_jpegd[] = {
+       12, 13, 14, 40, 66, 67
+};
+
+static const char trans_tbl_h264d[] = {
+       12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
+       28, 29, 40
+};
+
+static const char trans_tbl_vc1d[] = {
+       12, 13, 14, 15, 16, 17, 27, 41
+};
+
+static const char trans_tbl_vp6d[] = {
+       12, 13, 14, 18, 27, 40
+};
+
+static const char trans_tbl_vp8d[] = {
+       10, 12, 13, 14, 18, 19, 22, 23, 24, 25, 26, 27, 28, 29, 40
+};
+
+static struct mpp_trans_info trans_rk_vdpu1[] = {
+       [RKVDPU1_FMT_H264D] = {
+               .count = sizeof(trans_tbl_h264d),
+               .table = trans_tbl_h264d,
+       },
+       [RKVDPU1_FMT_MPEG4D] = {
+               .count = sizeof(trans_tbl_default),
+               .table = trans_tbl_default,
+       },
+       [RKVDPU1_FMT_H263D] = {
+               .count = sizeof(trans_tbl_default),
+               .table = trans_tbl_default,
+       },
+       [RKVDPU1_FMT_JPEGD] = {
+               .count = sizeof(trans_tbl_jpegd),
+               .table = trans_tbl_jpegd,
+       },
+       [RKVDPU1_FMT_VC1D] = {
+               .count = sizeof(trans_tbl_vc1d),
+               .table = trans_tbl_vc1d,
+       },
+       [RKVDPU1_FMT_MPEG2D] = {
+               .count = sizeof(trans_tbl_default),
+               .table = trans_tbl_default,
+       },
+       [RKVDPU1_FMT_MPEG1D] = {
+               .count = sizeof(trans_tbl_default),
+               .table = trans_tbl_default,
+       },
+       [RKVDPU1_FMT_VP6D] = {
+               .count = sizeof(trans_tbl_vp6d),
+               .table = trans_tbl_vp6d,
+       },
+       [RKVDPU1_FMT_RESERVED] = {
+               .count = 0,
+               .table = NULL,
+       },
+       [RKVDPU1_FMT_VP7D] = {
+               .count = sizeof(trans_tbl_default),
+               .table = trans_tbl_default,
+       },
+       [RKVDPU1_FMT_VP8D] = {
+               .count = sizeof(trans_tbl_vp8d),
+               .table = trans_tbl_vp8d,
+       },
+       [RKVDPU1_FMT_AVSD] = {
+               .count = sizeof(trans_tbl_avsd),
+               .table = trans_tbl_avsd,
+       },
+};
+
+static const struct vdpu1_dev_data vdpu_v1_data = {
+       .dec_data = {
+               /* Exclude the register of the Post Processor */
+               .reg_len = 60,
+               .trans_info = trans_rk_vdpu1,
+               .node_name = RKVDPU1_NODE_NAME,
+       },
+       .pp_support = false,
+};
+
+static const struct vdpu1_dev_data vdpu_pp_v1_data = {
+       .dec_data = {
+               /* Exclude the register of the Performance monitor */
+               .reg_len = 101,
+               .trans_info = trans_rk_vdpu1,
+               .node_name = RKVDPU1_NODE_NAME,
+       },
+       .pp_support = true,
+};
+
+static const struct vdpu1_dev_data vdpu_avs_v1_data = {
+       .dec_data = {
+               /* Exclude the register of the Performance monitor */
+               .reg_len = 101,
+               .trans_info = trans_rk_vdpu1,
+               .node_name = RKAVSD1_NODE_NAME,
+       },
+       .pp_support = true,
+};
+
+static void *rockchip_rkvdpu1_get_drv_data(struct platform_device *pdev);
+
+static void *rockchip_mpp_rkvdpu_alloc_task(struct mpp_session *session,
+                                           void __user *src, u32 size)
+{
+       struct rkvdpu_task *task = NULL;
+       u32 reg_len;
+       u32 extinf_len;
+       u32 fmt = 0;
+       u32 dwsize = size / sizeof(u32);
+       int err = -EFAULT;
+
+       mpp_debug_enter();
+
+       task = kzalloc(sizeof(*task), GFP_KERNEL);
+       if (!task)
+               return NULL;
+
+       mpp_dev_task_init(session, &task->mpp_task);
+
+       reg_len = dwsize > ROCKCHIP_VDPU1_REG_NUM ?
+               ROCKCHIP_VDPU1_REG_NUM : dwsize;
+       extinf_len = dwsize > reg_len ? (dwsize - reg_len) * 4 : 0;
+
+       if (copy_from_user(task->reg, src, reg_len * 4)) {
+               mpp_err("error: copy_from_user failed in reg_init\n");
+               err = -EFAULT;
+               goto fail;
+       }
+
+       fmt = RKVDPU1_GET_FORMAT(task->reg[RKVDPU1_REG_SYS_CTRL_INDEX]);
+       if (extinf_len > 0) {
+               if (likely(fmt == RKVDPU1_FMT_JPEGD)) {
+                       err = copy_from_user(&task->ext_inf,
+                                            (u8 *)src + size
+                                            - JPEG_IOC_EXTRA_SIZE,
+                                            JPEG_IOC_EXTRA_SIZE);
+               } else {
+                       u32 ext_cpy = min_t(size_t, extinf_len,
+                                           sizeof(task->ext_inf));
+                       err = copy_from_user(&task->ext_inf,
+                                            (u32 *)src + reg_len, ext_cpy);
+               }
+
+               if (err) {
+                       mpp_err("copy_from_user failed when extra info\n");
+                       err = -EFAULT;
+                       goto fail;
+               }
+       }
+
+       err = mpp_reg_address_translate(session->mpp, &task->mpp_task, fmt,
+                                       task->reg);
+       if (err) {
+               mpp_err("error: translate reg address failed.\n");
+
+               if (unlikely(debug & DEBUG_DUMP_ERR_REG))
+                       mpp_debug_dump_reg_mem(task->reg,
+                                              ROCKCHIP_VDPU1_REG_NUM);
+               goto fail;
+       }
+       /*
+        * special offset scale case
+        *
+        * This translation is for fd + offset translation.
+        * One register has 32bits. We need to transfer both buffer file
+        * handle and the start address offset so we packet file handle
+        * and offset together using below format.
+        *
+        *  0~9  bit for buffer file handle range 0 ~ 1023
+        * 10~31 bit for offset range 0 ~ 4M
+        *
+        * But on 4K case the offset can be larger the 4M
+        */
+       if (likely(fmt == RKVDPU1_FMT_H264D)) {
+               struct mpp_mem_region *mem_region = NULL;
+               dma_addr_t iova = 0;
+               u32 offset = task->reg[RKVDPU1_REG_DIR_MV_BASE_INDEX];
+               int fd = task->reg[RKVDPU1_REG_DIR_MV_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;
+               mpp_debug(DEBUG_IOMMU, "DMV[%3d]: %3d => %pad + offset %10d\n",
+                         RKVDPU1_REG_DIR_MV_BASE_INDEX, fd, &iova, offset);
+               task->reg[RKVDPU1_REG_DIR_MV_BASE_INDEX] = iova + offset;
+       }
+
+       task->strm_base = task->reg[RKVDPU1_REG_STREAM_RLC_BASE_INDEX];
+
+       mpp_debug(DEBUG_SET_REG, "extra info cnt %u, magic %08x",
+                 task->ext_inf.cnt, task->ext_inf.magic);
+       mpp_translate_extra_info(&task->mpp_task, &task->ext_inf, task->reg);
+
+       mpp_debug_leave();
+
+       return &task->mpp_task;
+
+fail:
+       mpp_dev_task_finalize(session, &task->mpp_task);
+       kfree(task);
+       return ERR_PTR(err);
+}
+
+static int rockchip_mpp_rkvdpu_prepare(struct rockchip_mpp_dev *mpp_dev,
+                                      struct mpp_task *task)
+{
+       return -EINVAL;
+}
+
+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;
+
+       mpp_dev_write_seq(mpp_dev, RKVDPU1_REG_DEC_DEV_CTRL,
+                         &task->reg[RKVDPU1_REG_DEC_DEV_CTRL_INDEX],
+                         mpp_dev->variant->reg_len
+                         - RKVDPU1_REG_DEC_DEV_CTRL_INDEX);
+       /* Flush the registers */
+       wmb();
+       mpp_dev_write(mpp_dev, RKVDPU1_REG_DEC_INT_EN,
+                     task->reg[RKVDPU1_REG_DEC_INT_EN_INDEX]
+                     | RKVDPU1_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();
+
+       mpp_dev_read_seq(mpp_dev, RKVDPU1_REG_DEC_DEV_CTRL,
+                        &task->reg[RKVDPU1_REG_DEC_DEV_CTRL_INDEX],
+                        mpp_dev->variant->reg_len
+                        - RKVDPU1_REG_DEC_DEV_CTRL);
+       task->reg[RKVDPU1_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);
+
+       /* FIXME may overflow the kernel */
+       if (copy_to_user(dst, task->reg, size)) {
+               mpp_err("copy_to_user failed\n");
+               return -EIO;
+       }
+
+       return 0;
+}
+
+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, RKVDPU1_REG_DEC_INT_EN);
+       if (!(irq_status & RKVDPU1_DEC_INT_RAW))
+               return IRQ_NONE;
+
+       mpp_dev_write(mpp_dev, RKVDPU1_REG_DEC_INT_EN, 0);
+       /* 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 = RKVDPU1_INT_TIMEOUT
+               | RKVDPU1_INT_STRM_ERROR
+               | RKVDPU1_INT_ASO_ERROR
+               | RKVDPU1_INT_BUF_EMPTY
+               | RKVDPU1_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;
+
+       /* TODO: use devm_reset_control_get_share() instead */
+       dec_dev->rst_a = devm_reset_control_get(mpp_dev->dev, "video_a");
+       dec_dev->rst_h = devm_reset_control_get(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;
+       }
+
+       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");
+
+               /* Don't skip this or iommu won't work after reset */
+               rockchip_pmu_idle_request(mpp_dev->dev, true);
+               safe_reset(dec->rst_a);
+               safe_reset(dec->rst_h);
+               udelay(5);
+               safe_unreset(dec->rst_h);
+               safe_unreset(dec->rst_a);
+               rockchip_pmu_idle_request(mpp_dev->dev, false);
+
+               mpp_dev_write(mpp_dev, RKVDPU1_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;
+
+       dec_dev->data = rockchip_rkvdpu1_get_drv_data(pdev);
+       mpp_dev = &dec_dev->mpp_dev;
+       mpp_dev->variant = &dec_dev->data->dec_data;
+       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);
+
+       ret = mpp_dev_register_node(mpp_dev, mpp_dev->variant->node_name, NULL);
+       if (ret)
+               dev_err(dev, "register char device failed: %d\n", ret);
+
+       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_rkvdpu1_dt_match[] = {
+       { .compatible = "rockchip,vpu-decoder-v1", .data = &vdpu_v1_data},
+       { .compatible = "rockchip,vpu-decoder-pp-v1", .data = &vdpu_pp_v1_data},
+       { .compatible = "rockchip,avs-decoder-v1", .data = &vdpu_avs_v1_data},
+       {},
+};
+
+static void *rockchip_rkvdpu1_get_drv_data(struct platform_device *pdev)
+{
+       struct vdpu1_dev_data *driver_data = NULL;
+
+       if (pdev->dev.of_node) {
+               const struct of_device_id *match;
+
+               match = of_match_node(mpp_rkvdpu1_dt_match,
+                                     pdev->dev.of_node);
+               if (match)
+                       driver_data = (struct vdpu1_dev_data *)match->data;
+       }
+       return driver_data;
+}
+
+static struct platform_driver rockchip_rkvdpu1_driver = {
+       .probe = rockchip_mpp_rkvdpu_probe,
+       .remove = rockchip_mpp_rkvdpu_remove,
+       .driver = {
+               .name = RKVDPU1_DRIVER_NAME,
+               .of_match_table = of_match_ptr(mpp_rkvdpu1_dt_match),
+       },
+};
+
+static int __init mpp_dev_rkvdpu1_init(void)
+{
+       int ret = platform_driver_register(&rockchip_rkvdpu1_driver);
+
+       if (ret) {
+               mpp_err("Platform device register failed (%d).\n", ret);
+               return ret;
+       }
+
+       return ret;
+}
+
+static void __exit mpp_dev_rkvdpu1_exit(void)
+{
+       platform_driver_unregister(&rockchip_rkvdpu1_driver);
+}
+
+module_init(mpp_dev_rkvdpu1_init);
+module_exit(mpp_dev_rkvdpu1_exit);
+
+MODULE_DEVICE_TABLE(of, mpp_rkvdpu1_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..b131790f72a3
--- /dev/null
+++ b/drivers/staging/rockchip-mpp/mpp_dev_vdpu2.c
@@ -0,0 +1,577 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd
+ *             Randy Li, <ay...@soulik.info>
+ *
+ * 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 "mpp_debug.h"
+#include "mpp_dev_common.h"
+
+#define RKVDPU2_DRIVER_NAME            "mpp_vdpu2"
+#define RKVDPU2_NODE_NAME              "vpu-service"
+
+/* 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_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_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)
+
+#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)
+
+#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;
+       struct extra_info_for_iommu ext_inf;
+
+       u32 strm_base;
+       u32 irq_status;
+};
+
+/*
+ * 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
+};
+
+static struct mpp_trans_info trans_rk_vdpu2[] = {
+       [RKVDPU2_FMT_H264D] = {
+               .count = sizeof(trans_tbl_h264d),
+               .table = trans_tbl_h264d,
+       },
+       [RKVDPU2_FMT_H263D] = {
+               .count = sizeof(trans_tbl_default),
+               .table = trans_tbl_default,
+       },
+       [RKVDPU2_FMT_MPEG4D] = {
+               .count = sizeof(trans_tbl_default),
+               .table = trans_tbl_default,
+       },
+       [RKVDPU2_FMT_JPEGD] = {
+               .count = sizeof(trans_tbl_jpegd),
+               .table = trans_tbl_jpegd,
+       },
+       [RKVDPU2_FMT_VC1D] = {
+               .count = sizeof(trans_tbl_vc1d),
+               .table = trans_tbl_vc1d,
+       },
+       [RKVDPU2_FMT_MPEG2D] = {
+               .count = sizeof(trans_tbl_default),
+               .table = trans_tbl_default,
+       },
+       [RKVDPU2_FMT_MPEG1D] = {
+               .count = sizeof(trans_tbl_default),
+               .table = trans_tbl_default,
+       },
+       [RKVDPU2_FMT_VP6D] = {
+               .count = sizeof(trans_tbl_vp6d),
+               .table = trans_tbl_vp6d,
+       },
+       [RKVDPU2_FMT_RESERVED] = {
+               .count = 0,
+               .table = NULL,
+       },
+       [RKVDPU2_FMT_VP7D] = {
+               .count = sizeof(trans_tbl_default),
+               .table = trans_tbl_default,
+       },
+       [RKVDPU2_FMT_VP8D] = {
+               .count = sizeof(trans_tbl_vp8d),
+               .table = trans_tbl_vp8d,
+       },
+       [RKVDPU2_FMT_AVSD] = {
+               .count = sizeof(trans_tbl_default),
+               .table = trans_tbl_default,
+       },
+};
+
+static const struct mpp_dev_variant rkvdpu_v2_data = {
+       /* Exclude the register of the Performance counter */
+       .reg_len = 159,
+       .trans_info = trans_rk_vdpu2,
+       .node_name = RKVDPU2_NODE_NAME,
+};
+
+static void *rockchip_rkvdpu2_get_drv_data(struct platform_device *pdev);
+
+static void *rockchip_mpp_rkvdpu_alloc_task(struct mpp_session *session,
+                                           void __user *src, u32 size)
+{
+       struct rkvdpu_task *task = NULL;
+       u32 reg_len;
+       u32 extinf_len;
+       u32 fmt = 0;
+       u32 dwsize = size / sizeof(u32);
+       int err = -EFAULT;
+
+       mpp_debug_enter();
+
+       task = kzalloc(sizeof(*task), GFP_KERNEL);
+       if (!task)
+               return NULL;
+
+       mpp_dev_task_init(session, &task->mpp_task);
+
+       reg_len = dwsize > ROCKCHIP_VDPU2_REG_NUM ?
+               ROCKCHIP_VDPU2_REG_NUM : dwsize;
+       extinf_len = dwsize > reg_len ? (dwsize - reg_len) * 4 : 0;
+
+       if (copy_from_user(task->reg, src, reg_len * 4)) {
+               mpp_err("error: copy_from_user failed in reg_init\n");
+               err = -EFAULT;
+               goto fail;
+       }
+
+       fmt = RKVDPU2_GET_FORMAT(task->reg[RKVDPU2_REG_SYS_CTRL_INDEX]);
+       if (extinf_len > 0) {
+               if (likely(fmt == RKVDPU2_FMT_JPEGD)) {
+                       err = copy_from_user(&task->ext_inf,
+                                            (u8 *)src + size
+                                            - JPEG_IOC_EXTRA_SIZE,
+                                            JPEG_IOC_EXTRA_SIZE);
+               } else {
+                       u32 ext_cpy = min_t(size_t, extinf_len,
+                                           sizeof(task->ext_inf));
+                       err = copy_from_user(&task->ext_inf,
+                                            (u32 *)src + reg_len, ext_cpy);
+               }
+
+               if (err) {
+                       mpp_err("copy_from_user failed when extra info\n");
+                       err = -EFAULT;
+                       goto fail;
+               }
+       }
+
+       err = mpp_reg_address_translate(session->mpp, &task->mpp_task, fmt,
+                                       task->reg);
+       if (err) {
+               mpp_err("error: translate reg address failed.\n");
+
+               if (unlikely(debug & DEBUG_DUMP_ERR_REG))
+                       mpp_debug_dump_reg_mem(task->reg,
+                                              ROCKCHIP_VDPU2_REG_NUM);
+               goto fail;
+       }
+
+       if (likely(fmt == RKVDPU2_FMT_H264D)) {
+               struct mpp_mem_region *mem_region = NULL;
+               dma_addr_t iova = 0;
+               u32 offset = task->reg[RKVDPU2_REG_DIR_MV_BASE_INDEX];
+               int fd = task->reg[RKVDPU2_REG_DIR_MV_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;
+               mpp_debug(DEBUG_IOMMU, "DMV[%3d]: %3d => %pad + offset %10d\n",
+                         RKVDPU2_REG_DIR_MV_BASE_INDEX, fd, &iova, offset);
+               task->reg[RKVDPU2_REG_DIR_MV_BASE_INDEX] = iova + offset;
+       }
+
+       task->strm_base = task->reg[RKVDPU2_REG_STREAM_RLC_BASE_INDEX];
+
+       mpp_debug(DEBUG_SET_REG, "extra info cnt %u, magic %08x",
+                 task->ext_inf.cnt, task->ext_inf.magic);
+       mpp_translate_extra_info(&task->mpp_task, &task->ext_inf, task->reg);
+
+       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);
+
+       mpp_dev_task_finalize(session, &task->mpp_task);
+       kfree(task);
+       return ERR_PTR(err);
+}
+
+static int rockchip_mpp_rkvdpu_prepare(struct rockchip_mpp_dev *mpp_dev,
+                                      struct mpp_task *task)
+{
+       return -EINVAL;
+}
+
+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);
+
+       /* FIXME may overflow the kernel */
+       if (copy_to_user(dst, task->reg, size)) {
+               mpp_err("copy_to_user failed\n");
+               return -EIO;
+       }
+
+       return 0;
+}
+
+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;
+
+       /* TODO: use devm_reset_control_get_share() instead */
+       dec_dev->rst_a = devm_reset_control_get(mpp_dev->dev, "video_a");
+       dec_dev->rst_h = devm_reset_control_get(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;
+       }
+
+       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);
+
+       ret = mpp_dev_register_node(mpp_dev, mpp_dev->variant->node_name, NULL);
+       if (ret)
+               dev_err(dev, "register char device failed: %d\n", ret);
+
+       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_dev_vepu1.c 
b/drivers/staging/rockchip-mpp/mpp_dev_vepu1.c
new file mode 100644
index 000000000000..64619092c792
--- /dev/null
+++ b/drivers/staging/rockchip-mpp/mpp_dev_vepu1.c
@@ -0,0 +1,481 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd
+ *             Randy Li, <ay...@soulik.info>
+ *
+ * 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 "mpp_debug.h"
+#include "mpp_dev_common.h"
+
+#define RKVEPU1_DRIVER_NAME            "mpp_vepu1"
+#define RKVEPU1_NODE_NAME              "vepu"
+
+/* The maximum registers number of all the version */
+#define ROCKCHIP_VEPU1_REG_NUM                 (164)
+
+#define RKVEPU1_REG_INT                                0x004
+#define RKVEPU1_REG_INT_INDEX                  (1)
+#define                RKVEPU1_INT_SLICE               BIT(8)
+#define                RKVEPU1_INT_TIMEOUT             BIT(6)
+#define                RKVEPU1_INT_BUF_FULL            BIT(5)
+#define                RKVEPU1_INT_RESET               BIT(4)
+#define                RKVEPU1_INT_BUS_ERROR           BIT(3)
+#define                RKVEPU1_INT_RDY                 BIT(2)
+#define                RKVEPU1_IRQ_DIS                 BIT(1)
+#define                RKVEPU1_INT_RAW                 BIT(0)
+
+#define RKVEPU1_REG_AXI_CTRL                   0x008
+#define RKVEPU1_REG_AXI_CTRL_INDEX             (2)
+#define                RKVEPU1_CLOCK_GATE_EN           BIT(4)
+
+#define RKVEPU1_REG_ENC_CTRL                   0x038
+#define RKVEPU1_REG_ENC_CTRL_INDEX             (14)
+#define                RKVEPU1_INT_TIMEOUT_EN          BIT(31)
+#define                RKVEPU1_INT_SLICE_EN            BIT(28)
+#define                RKVEPU1_GET_FORMAT(x)           (((x) >> 1) & 0x3)
+#define                RKVEPU1_FMT_RESERVED            (0)
+#define                RKVEPU1_FMT_VP8E                (1)
+#define                RKVEPU1_FMT_JPEGE               (2)
+#define                RKVEPU1_FMT_H264E               (3)
+#define                RKVEPU1_ENC_START               BIT(0)
+
+#define RKVEPU1_REG_INPUT_CTRL                 0x03c
+#define RKVEPU1_REG_INPUT_CTRL_INDEX           (15)
+
+#define to_rkvepu_task(ctx)            \
+               container_of(ctx, struct rkvepu_task, mpp_task)
+#define to_rkvepu_dev(dev)             \
+               container_of(dev, struct rockchip_rkvepu_dev, mpp_dev)
+
+static int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "bit switch for vepu1 debug information");
+
+struct rockchip_rkvepu_dev {
+       struct rockchip_mpp_dev mpp_dev;
+
+       struct reset_control *rst_a;
+       struct reset_control *rst_h;
+
+       void *current_task;
+};
+
+struct rkvepu_task {
+       struct mpp_task mpp_task;
+
+       u32 reg[ROCKCHIP_VEPU1_REG_NUM];
+       u32 idx;
+       struct extra_info_for_iommu ext_inf;
+
+       u32 strm_base;
+       u32 irq_status;
+};
+
+/*
+ * file handle translate information
+ */
+static const char trans_tbl_default[] = {
+       5, 6, 7, 8, 9, 10, 11, 12, 13, 51
+};
+
+static const char trans_tbl_vp8e[] = {
+       5, 6, 7, 8, 9, 10, 11, 12, 13, 16, 17, 26, 51, 52, 58, 59
+};
+
+static struct mpp_trans_info trans_rk_vepu1[] = {
+       [RKVEPU1_FMT_RESERVED] = {
+               .count = 0,
+               .table = NULL,
+       },
+       [RKVEPU1_FMT_VP8E] = {
+               .count = sizeof(trans_tbl_vp8e),
+               .table = trans_tbl_vp8e,
+       },
+       [RKVEPU1_FMT_JPEGE] = {
+               .count = sizeof(trans_tbl_default),
+               .table = trans_tbl_default,
+       },
+       [RKVEPU1_FMT_H264E] = {
+               .count = sizeof(trans_tbl_default),
+               .table = trans_tbl_default,
+       },
+};
+
+static const struct mpp_dev_variant rkvepu_v1_data = {
+       .reg_len = ROCKCHIP_VEPU1_REG_NUM,
+       .trans_info = trans_rk_vepu1,
+       .node_name = RKVEPU1_NODE_NAME,
+};
+
+static void *rockchip_rkvepu1_get_drv_data(struct platform_device *pdev);
+
+static void *rockchip_mpp_rkvepu_alloc_task(struct mpp_session *session,
+                                           void __user *src, u32 size)
+{
+       struct rkvepu_task *task = NULL;
+       u32 reg_len;
+       u32 extinf_len;
+       u32 fmt = 0;
+       u32 dwsize = size / sizeof(u32);
+       int err = -EFAULT;
+
+       mpp_debug_enter();
+
+       task = kzalloc(sizeof(*task), GFP_KERNEL);
+       if (!task)
+               return NULL;
+
+       mpp_dev_task_init(session, &task->mpp_task);
+
+       reg_len = dwsize > ROCKCHIP_VEPU1_REG_NUM ?
+               ROCKCHIP_VEPU1_REG_NUM : dwsize;
+       extinf_len = dwsize > reg_len ? (dwsize - reg_len) * 4 : 0;
+
+       if (copy_from_user(task->reg, src, reg_len * 4)) {
+               mpp_err("error: copy_from_user failed in reg_init\n");
+               err = -EFAULT;
+               goto fail;
+       }
+
+       fmt = RKVEPU1_GET_FORMAT(task->reg[RKVEPU1_REG_ENC_CTRL_INDEX]);
+       if (extinf_len > 0) {
+               if (likely(fmt == RKVEPU1_FMT_JPEGE)) {
+                       err = copy_from_user(&task->ext_inf,
+                                            (u8 *)src + size
+                                            - JPEG_IOC_EXTRA_SIZE,
+                                            JPEG_IOC_EXTRA_SIZE);
+               } else {
+                       u32 ext_cpy = min_t(size_t, extinf_len,
+                                           sizeof(task->ext_inf));
+                       err = copy_from_user(&task->ext_inf,
+                                            (u32 *)src + reg_len, ext_cpy);
+               }
+
+               if (err) {
+                       mpp_err("copy_from_user failed when extra info\n");
+                       err = -EFAULT;
+                       goto fail;
+               }
+       }
+
+       err = mpp_reg_address_translate(session->mpp, &task->mpp_task, fmt,
+                                       task->reg);
+       if (err) {
+               mpp_err("error: translate reg address failed.\n");
+
+               if (unlikely(debug & DEBUG_DUMP_ERR_REG))
+                       mpp_debug_dump_reg_mem(task->reg,
+                                              ROCKCHIP_VEPU1_REG_NUM);
+               goto fail;
+       }
+
+       mpp_debug(DEBUG_SET_REG, "extra info cnt %u, magic %08x",
+                 task->ext_inf.cnt, task->ext_inf.magic);
+       mpp_translate_extra_info(&task->mpp_task, &task->ext_inf, task->reg);
+
+       mpp_debug_leave();
+
+       return &task->mpp_task;
+
+fail:
+       mpp_dev_task_finalize(session, &task->mpp_task);
+       kfree(task);
+       return ERR_PTR(err);
+}
+
+static int rockchip_mpp_rkvepu_prepare(struct rockchip_mpp_dev *mpp_dev,
+                                      struct mpp_task *task)
+{
+       return -EINVAL;
+}
+
+static int rockchip_mpp_rkvepu_run(struct rockchip_mpp_dev *mpp_dev,
+                                  struct mpp_task *mpp_task)
+{
+       struct rkvepu_task *task = NULL;
+       struct rockchip_rkvepu_dev *enc_dev = NULL;
+
+       mpp_debug_enter();
+
+       task = to_rkvepu_task(mpp_task);
+       enc_dev = to_rkvepu_dev(mpp_dev);
+
+       /* FIXME: spin lock here */
+       enc_dev->current_task = task;
+
+       mpp_dev_write_seq(mpp_dev, RKVEPU1_REG_AXI_CTRL,
+                         &task->reg[RKVEPU1_REG_AXI_CTRL_INDEX],
+                         RKVEPU1_REG_ENC_CTRL_INDEX
+                         - RKVEPU1_REG_AXI_CTRL_INDEX);
+
+       mpp_dev_write_seq(mpp_dev, RKVEPU1_REG_INPUT_CTRL,
+                         &task->reg[RKVEPU1_REG_INPUT_CTRL_INDEX],
+                         mpp_dev->variant->reg_len
+                         - RKVEPU1_REG_INPUT_CTRL_INDEX);
+       /* Flush the registers */
+       wmb();
+       mpp_dev_write(mpp_dev, RKVEPU1_REG_ENC_CTRL,
+                     task->reg[RKVEPU1_REG_ENC_CTRL_INDEX]
+                     | RKVEPU1_ENC_START);
+
+       mpp_debug_leave();
+
+       return 0;
+}
+
+static int rockchip_mpp_rkvepu_finish(struct rockchip_mpp_dev *mpp_dev,
+                                     struct mpp_task *mpp_task)
+{
+       struct rkvepu_task *task = to_rkvepu_task(mpp_task);
+
+       mpp_debug_enter();
+
+       task->reg[RKVEPU1_REG_INT_INDEX] = task->irq_status;
+
+       mpp_debug_leave();
+
+       return 0;
+}
+
+static int rockchip_mpp_rkvepu_result(struct rockchip_mpp_dev *mpp_dev,
+                                     struct mpp_task *mpp_task,
+                                     u32 __user *dst, u32 size)
+{
+       struct rkvepu_task *task = to_rkvepu_task(mpp_task);
+
+       /* FIXME may overflow the kernel */
+       if (copy_to_user(dst, task->reg, size)) {
+               mpp_err("copy_to_user failed\n");
+               return -EIO;
+       }
+
+       return 0;
+}
+
+static int rockchip_mpp_rkvepu_free_task(struct mpp_session *session,
+                                        struct mpp_task *mpp_task)
+{
+       struct rkvepu_task *task = to_rkvepu_task(mpp_task);
+
+       mpp_dev_task_finalize(session, mpp_task);
+       kfree(task);
+
+       return 0;
+}
+
+static irqreturn_t mpp_rkvepu_isr(int irq, void *dev_id)
+{
+       struct rockchip_rkvepu_dev *enc_dev = dev_id;
+       struct rockchip_mpp_dev *mpp_dev = &enc_dev->mpp_dev;
+       struct rkvepu_task *task = NULL;
+       struct mpp_task *mpp_task = NULL;
+       u32 irq_status;
+       u32 err_mask;
+
+       irq_status = mpp_dev_read(mpp_dev, RKVEPU1_REG_INT);
+       if (!(irq_status & RKVEPU1_INT_RAW))
+               return IRQ_NONE;
+
+       mpp_dev_write(mpp_dev, RKVEPU1_REG_INT, 0);
+       /* FIXME use a spin lock here */
+       task = (struct rkvepu_task *)enc_dev->current_task;
+       if (!task) {
+               dev_err(enc_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 = RKVEPU1_INT_TIMEOUT
+               | RKVEPU1_INT_BUF_FULL
+               | RKVEPU1_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;
+
+       return IRQ_NONE;
+}
+
+static int rockchip_mpp_rkvepu_assign_reset(struct rockchip_rkvepu_dev 
*enc_dev)
+{
+       struct rockchip_mpp_dev *mpp_dev = &enc_dev->mpp_dev;
+
+       /* TODO: use devm_reset_control_get_share() instead */
+       enc_dev->rst_a = devm_reset_control_get(mpp_dev->dev, "video_a");
+       enc_dev->rst_h = devm_reset_control_get(mpp_dev->dev, "video_h");
+
+       if (IS_ERR_OR_NULL(enc_dev->rst_a)) {
+               mpp_err("No aclk reset resource define\n");
+               enc_dev->rst_a = NULL;
+       }
+
+       if (IS_ERR_OR_NULL(enc_dev->rst_h)) {
+               mpp_err("No hclk reset resource define\n");
+               enc_dev->rst_h = NULL;
+       }
+
+       return 0;
+}
+
+static int rockchip_mpp_rkvepu_reset(struct rockchip_mpp_dev *mpp_dev)
+{
+       struct rockchip_rkvepu_dev *enc = to_rkvepu_dev(mpp_dev);
+
+       if (enc->rst_a && enc->rst_h) {
+               mpp_debug(DEBUG_RESET, "reset in\n");
+
+               /* Don't skip this or iommu won't work after reset */
+               rockchip_pmu_idle_request(mpp_dev->dev, true);
+               safe_reset(enc->rst_a);
+               safe_reset(enc->rst_h);
+               udelay(5);
+               safe_unreset(enc->rst_h);
+               safe_unreset(enc->rst_a);
+               rockchip_pmu_idle_request(mpp_dev->dev, false);
+
+               mpp_dev_write(mpp_dev, RKVEPU1_REG_ENC_CTRL, 0);
+               enc->current_task = NULL;
+               mpp_debug(DEBUG_RESET, "reset out\n");
+       }
+
+       return 0;
+}
+
+static struct mpp_dev_ops rkvepu_ops = {
+       .alloc_task = rockchip_mpp_rkvepu_alloc_task,
+       .prepare = rockchip_mpp_rkvepu_prepare,
+       .run = rockchip_mpp_rkvepu_run,
+       .finish = rockchip_mpp_rkvepu_finish,
+       .result = rockchip_mpp_rkvepu_result,
+       .free_task = rockchip_mpp_rkvepu_free_task,
+       .reset = rockchip_mpp_rkvepu_reset,
+};
+
+static int rockchip_mpp_rkvepu_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct rockchip_rkvepu_dev *enc_dev = NULL;
+       struct rockchip_mpp_dev *mpp_dev = NULL;
+       int ret = 0;
+
+       enc_dev = devm_kzalloc(dev, sizeof(struct rockchip_rkvepu_dev),
+                              GFP_KERNEL);
+       if (!enc_dev)
+               return -ENOMEM;
+
+       mpp_dev = &enc_dev->mpp_dev;
+       mpp_dev->variant = rockchip_rkvepu1_get_drv_data(pdev);
+       ret = mpp_dev_common_probe(mpp_dev, pdev, &rkvepu_ops);
+       if (ret)
+               return ret;
+
+       ret = devm_request_threaded_irq(dev, mpp_dev->irq, NULL, mpp_rkvepu_isr,
+                                       IRQF_SHARED | IRQF_ONESHOT,
+                                       dev_name(dev), enc_dev);
+       if (ret) {
+               dev_err(dev, "register interrupter runtime failed\n");
+               return ret;
+       }
+
+       rockchip_mpp_rkvepu_assign_reset(enc_dev);
+
+       ret = mpp_dev_register_node(mpp_dev, mpp_dev->variant->node_name, NULL);
+       if (ret)
+               dev_err(dev, "register char device failed: %d\n", ret);
+
+       dev_info(dev, "probing finish\n");
+
+       platform_set_drvdata(pdev, enc_dev);
+
+       return 0;
+}
+
+static int rockchip_mpp_rkvepu_remove(struct platform_device *pdev)
+{
+       struct rockchip_rkvepu_dev *enc_dev = platform_get_drvdata(pdev);
+
+       mpp_dev_common_remove(&enc_dev->mpp_dev);
+
+       return 0;
+}
+
+static const struct of_device_id mpp_rkvepu1_dt_match[] = {
+       { .compatible = "rockchip,vpu-encoder-v1", .data = &rkvepu_v1_data},
+       {},
+};
+
+static void *rockchip_rkvepu1_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_rkvepu1_dt_match, pdev->dev.of_node);
+               if (match)
+                       driver_data = (struct mpp_dev_variant *)match->data;
+       }
+       return driver_data;
+}
+
+static struct platform_driver rockchip_rkvepu1_driver = {
+       .probe = rockchip_mpp_rkvepu_probe,
+       .remove = rockchip_mpp_rkvepu_remove,
+       .driver = {
+               .name = RKVEPU1_DRIVER_NAME,
+               .of_match_table = of_match_ptr(mpp_rkvepu1_dt_match),
+       },
+};
+
+static int __init mpp_dev_rkvepu1_init(void)
+{
+       int ret = platform_driver_register(&rockchip_rkvepu1_driver);
+
+       if (ret) {
+               mpp_err("Platform device register failed (%d).\n", ret);
+               return ret;
+       }
+
+       return ret;
+}
+
+static void __exit mpp_dev_rkvepu1_exit(void)
+{
+       platform_driver_unregister(&rockchip_rkvepu1_driver);
+}
+
+module_init(mpp_dev_rkvepu1_init);
+module_exit(mpp_dev_rkvepu1_exit);
+
+MODULE_DEVICE_TABLE(of, mpp_rkvepu1_dt_match);
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/staging/rockchip-mpp/mpp_dev_vepu2.c 
b/drivers/staging/rockchip-mpp/mpp_dev_vepu2.c
new file mode 100644
index 000000000000..48ec401145d5
--- /dev/null
+++ b/drivers/staging/rockchip-mpp/mpp_dev_vepu2.c
@@ -0,0 +1,478 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd
+ *             Randy Li, <ay...@soulik.info>
+ *
+ * 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 "mpp_debug.h"
+#include "mpp_dev_common.h"
+
+#define RKVEPU2_DRIVER_NAME            "mpp_vepu2"
+#define RKVEPU2_NODE_NAME              "vepu"
+
+/* The maximum registers number of all the version */
+#define ROCKCHIP_VEPU2_REG_NUM                 (184)
+
+#define RKVEPU2_REG_ENC_CTRL                   0x19c
+#define RKVEPU2_REG_ENC_CTRL_INDEX             (103)
+#define                RKVEPU2_GET_FORMAT(x)           (((x) >> 4) & 0x3)
+#define                RKVEPU2_FMT_RESERVED            (0)
+#define                RKVEPU2_FMT_VP8E                (1)
+#define                RKVEPU2_FMT_JPEGE               (2)
+#define                RKVEPU2_FMT_H264E               (3)
+#define                RKVEPU2_ENC_START               BIT(0)
+
+#define RKVEPU2_REG_MB_CTRL                    0x1a0
+#define RKVEPU2_REG_MB_CTRL_INDEX              (104)
+
+#define RKVEPU2_REG_INT                                0x1b4
+#define RKVEPU2_REG_INT_INDEX                  (109)
+#define                RKVEPU2_MV_SAD_WR_EN            BIT(24)
+#define                RKVEPU2_ROCON_WRITE_DIS         BIT(20)
+#define                RKVEPU1_INT_SLICE_EN            BIT(16)
+#define                RKVEPU2_CLOCK_GATE_EN           BIT(12)
+#define                RKVEPU2_INT_TIMEOUT_EN          BIT(10)
+#define                RKVEPU2_INT_CLEAR               BIT(9)
+#define                RKVEPU2_IRQ_DIS                 BIT(8)
+#define                RKVEPU2_INT_TIMEOUT             BIT(6)
+#define                RKVEPU2_INT_BUF_FULL            BIT(5)
+#define                RKVEPU2_INT_BUS_ERROR           BIT(4)
+#define                RKVEPU2_INT_SLICE               BIT(2)
+#define                RKVEPU2_INT_RDY                 BIT(1)
+#define                RKVEPU2_INT_RAW                 BIT(0)
+
+#define RKVPUE2_REG_DMV_4P_1P(i)               (0x1e0 + ((i) << 4))
+#define RKVPUE2_REG_DMV_4P_1P_INDEX(i)         (120 + (i))
+
+#define to_rkvepu_task(ctx)            \
+               container_of(ctx, struct rkvepu_task, mpp_task)
+#define to_rkvepu_dev(dev)             \
+               container_of(dev, struct rockchip_rkvepu_dev, mpp_dev)
+
+static int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "bit switch for vepu1 debug information");
+
+struct rockchip_rkvepu_dev {
+       struct rockchip_mpp_dev mpp_dev;
+
+       struct reset_control *rst_a;
+       struct reset_control *rst_h;
+
+       void *current_task;
+};
+
+struct rkvepu_task {
+       struct mpp_task mpp_task;
+
+       u32 reg[ROCKCHIP_VEPU2_REG_NUM];
+       u32 idx;
+       struct extra_info_for_iommu ext_inf;
+
+       u32 strm_base;
+       u32 irq_status;
+};
+
+/*
+ * file handle translate information
+ */
+static const char trans_tbl_default[] = {
+       48, 49, 50, 56, 57, 63, 64, 77, 78, 81
+};
+
+static struct mpp_trans_info trans_rk_vepu2[] = {
+       [RKVEPU2_FMT_RESERVED] = {
+               .count = 0,
+               .table = NULL,
+       },
+       [RKVEPU2_FMT_VP8E] = {
+               .count = sizeof(trans_tbl_default),
+               .table = trans_tbl_default,
+       },
+       [RKVEPU2_FMT_JPEGE] = {
+               .count = sizeof(trans_tbl_default),
+               .table = trans_tbl_default,
+       },
+       [RKVEPU2_FMT_H264E] = {
+               .count = sizeof(trans_tbl_default),
+               .table = trans_tbl_default,
+       },
+};
+
+static const struct mpp_dev_variant rkvepu_v2_data = {
+       .reg_len = ROCKCHIP_VEPU2_REG_NUM,
+       .trans_info = trans_rk_vepu2,
+       .node_name = RKVEPU2_NODE_NAME,
+};
+
+static void *rockchip_rkvepu2_get_drv_data(struct platform_device *pdev);
+
+static void *rockchip_mpp_rkvepu_alloc_task(struct mpp_session *session,
+                                           void __user *src, u32 size)
+{
+       struct rkvepu_task *task = NULL;
+       u32 reg_len;
+       u32 extinf_len;
+       u32 fmt = 0;
+       u32 dwsize = size / sizeof(u32);
+       int err = -EFAULT;
+
+       mpp_debug_enter();
+
+       task = kzalloc(sizeof(*task), GFP_KERNEL);
+       if (!task)
+               return NULL;
+
+       mpp_dev_task_init(session, &task->mpp_task);
+
+       reg_len = dwsize > ROCKCHIP_VEPU2_REG_NUM ?
+               ROCKCHIP_VEPU2_REG_NUM : dwsize;
+       extinf_len = dwsize > reg_len ? (dwsize - reg_len) * 4 : 0;
+
+       if (copy_from_user(task->reg, src, reg_len * 4)) {
+               mpp_err("error: copy_from_user failed in reg_init\n");
+               err = -EFAULT;
+               goto fail;
+       }
+
+       fmt = RKVEPU2_GET_FORMAT(task->reg[RKVEPU2_REG_ENC_CTRL_INDEX]);
+       if (extinf_len > 0) {
+               if (likely(fmt == RKVEPU2_FMT_JPEGE)) {
+                       err = copy_from_user(&task->ext_inf,
+                                            (u8 *)src + size
+                                            - JPEG_IOC_EXTRA_SIZE,
+                                            JPEG_IOC_EXTRA_SIZE);
+               } else {
+                       u32 ext_cpy = min_t(size_t, extinf_len,
+                                           sizeof(task->ext_inf));
+                       err = copy_from_user(&task->ext_inf,
+                                            (u32 *)src + reg_len, ext_cpy);
+               }
+
+               if (err) {
+                       mpp_err("copy_from_user failed when extra info\n");
+                       err = -EFAULT;
+                       goto fail;
+               }
+       }
+
+       err = mpp_reg_address_translate(session->mpp, &task->mpp_task, fmt,
+                                       task->reg);
+       if (err) {
+               mpp_err("error: translate reg address failed.\n");
+
+               if (unlikely(debug & DEBUG_DUMP_ERR_REG))
+                       mpp_debug_dump_reg_mem(task->reg,
+                                              ROCKCHIP_VEPU2_REG_NUM);
+               goto fail;
+       }
+
+       mpp_debug(DEBUG_SET_REG, "extra info cnt %u, magic %08x",
+                 task->ext_inf.cnt, task->ext_inf.magic);
+       mpp_translate_extra_info(&task->mpp_task, &task->ext_inf, task->reg);
+
+       mpp_debug_leave();
+
+       return &task->mpp_task;
+
+fail:
+       mpp_dev_task_finalize(session, &task->mpp_task);
+       kfree(task);
+       return ERR_PTR(err);
+}
+
+static int rockchip_mpp_rkvepu_prepare(struct rockchip_mpp_dev *mpp_dev,
+                                      struct mpp_task *task)
+{
+       return -EINVAL;
+}
+
+static int rockchip_mpp_rkvepu_run(struct rockchip_mpp_dev *mpp_dev,
+                                  struct mpp_task *mpp_task)
+{
+       struct rkvepu_task *task = NULL;
+       struct rockchip_rkvepu_dev *enc_dev = NULL;
+
+       mpp_debug_enter();
+
+       task = to_rkvepu_task(mpp_task);
+       enc_dev = to_rkvepu_dev(mpp_dev);
+
+       /* FIXME: spin lock here */
+       enc_dev->current_task = task;
+
+       mpp_dev_write_seq(mpp_dev, 0, &task->reg[0],
+                         RKVEPU2_REG_ENC_CTRL_INDEX);
+
+       mpp_dev_write_seq(mpp_dev, RKVEPU2_REG_MB_CTRL,
+                         &task->reg[RKVEPU2_REG_MB_CTRL_INDEX],
+                         ROCKCHIP_VEPU2_REG_NUM
+                         - RKVEPU2_REG_MB_CTRL_INDEX);
+       /* Flush the registers */
+       wmb();
+       mpp_dev_write(mpp_dev, RKVEPU2_REG_ENC_CTRL,
+                     task->reg[RKVEPU2_REG_ENC_CTRL_INDEX]
+                     | RKVEPU2_ENC_START);
+
+       mpp_debug_leave();
+
+       return 0;
+}
+
+static int rockchip_mpp_rkvepu_finish(struct rockchip_mpp_dev *mpp_dev,
+                                     struct mpp_task *mpp_task)
+{
+       struct rkvepu_task *task = to_rkvepu_task(mpp_task);
+
+       mpp_debug_enter();
+
+       task->reg[RKVEPU2_REG_INT_INDEX] = task->irq_status;
+
+       mpp_debug_leave();
+
+       return 0;
+}
+
+static int rockchip_mpp_rkvepu_result(struct rockchip_mpp_dev *mpp_dev,
+                                     struct mpp_task *mpp_task,
+                                     u32 __user *dst, u32 size)
+{
+       struct rkvepu_task *task = to_rkvepu_task(mpp_task);
+
+       /* FIXME may overflow the kernel */
+       if (copy_to_user(dst, task->reg, size)) {
+               mpp_err("copy_to_user failed\n");
+               return -EIO;
+       }
+
+       return 0;
+}
+
+static int rockchip_mpp_rkvepu_free_task(struct mpp_session *session,
+                                        struct mpp_task *mpp_task)
+{
+       struct rkvepu_task *task = to_rkvepu_task(mpp_task);
+
+       mpp_dev_task_finalize(session, mpp_task);
+       kfree(task);
+
+       return 0;
+}
+
+static irqreturn_t mpp_rkvepu_isr(int irq, void *dev_id)
+{
+       struct rockchip_rkvepu_dev *enc_dev = dev_id;
+       struct rockchip_mpp_dev *mpp_dev = &enc_dev->mpp_dev;
+       struct rkvepu_task *task = NULL;
+       struct mpp_task *mpp_task = NULL;
+       u32 irq_status;
+       u32 err_mask;
+
+       irq_status = mpp_dev_read(mpp_dev, RKVEPU2_REG_INT);
+       if (!(irq_status & RKVEPU2_INT_RAW))
+               return IRQ_NONE;
+
+       mpp_dev_write(mpp_dev, RKVEPU2_REG_INT, RKVEPU2_INT_CLEAR
+                     | RKVEPU2_CLOCK_GATE_EN);
+       /* FIXME use a spin lock here */
+       task = (struct rkvepu_task *)enc_dev->current_task;
+       if (!task) {
+               dev_err(enc_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 = RKVEPU2_INT_TIMEOUT
+               | RKVEPU2_INT_BUF_FULL
+               | RKVEPU2_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;
+
+       return IRQ_NONE;
+}
+
+static int rockchip_mpp_rkvepu_assign_reset(struct rockchip_rkvepu_dev 
*enc_dev)
+{
+       struct rockchip_mpp_dev *mpp_dev = &enc_dev->mpp_dev;
+
+       /* TODO: use devm_reset_control_get_share() instead */
+       enc_dev->rst_a = devm_reset_control_get(mpp_dev->dev, "video_a");
+       enc_dev->rst_h = devm_reset_control_get(mpp_dev->dev, "video_h");
+
+       if (IS_ERR_OR_NULL(enc_dev->rst_a)) {
+               mpp_err("No aclk reset resource define\n");
+               enc_dev->rst_a = NULL;
+       }
+
+       if (IS_ERR_OR_NULL(enc_dev->rst_h)) {
+               mpp_err("No hclk reset resource define\n");
+               enc_dev->rst_h = NULL;
+       }
+
+       return 0;
+}
+
+static int rockchip_mpp_rkvepu_reset(struct rockchip_mpp_dev *mpp_dev)
+{
+       struct rockchip_rkvepu_dev *enc = to_rkvepu_dev(mpp_dev);
+
+       if (enc->rst_a && enc->rst_h) {
+               mpp_debug(DEBUG_RESET, "reset in\n");
+
+               /* Don't skip this or iommu won't work after reset */
+               rockchip_pmu_idle_request(mpp_dev->dev, true);
+               safe_reset(enc->rst_a);
+               safe_reset(enc->rst_h);
+               udelay(5);
+               safe_unreset(enc->rst_h);
+               safe_unreset(enc->rst_a);
+               rockchip_pmu_idle_request(mpp_dev->dev, false);
+
+               mpp_dev_write(mpp_dev, RKVEPU2_REG_INT, RKVEPU2_INT_CLEAR);
+               enc->current_task = NULL;
+               mpp_debug(DEBUG_RESET, "reset out\n");
+       }
+
+       return 0;
+}
+
+static struct mpp_dev_ops rkvepu_ops = {
+       .alloc_task = rockchip_mpp_rkvepu_alloc_task,
+       .prepare = rockchip_mpp_rkvepu_prepare,
+       .run = rockchip_mpp_rkvepu_run,
+       .finish = rockchip_mpp_rkvepu_finish,
+       .result = rockchip_mpp_rkvepu_result,
+       .free_task = rockchip_mpp_rkvepu_free_task,
+       .reset = rockchip_mpp_rkvepu_reset,
+};
+
+static int rockchip_mpp_rkvepu_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct rockchip_rkvepu_dev *enc_dev = NULL;
+       struct rockchip_mpp_dev *mpp_dev = NULL;
+       int ret = 0;
+
+       enc_dev = devm_kzalloc(dev, sizeof(struct rockchip_rkvepu_dev),
+                              GFP_KERNEL);
+       if (!enc_dev)
+               return -ENOMEM;
+
+       mpp_dev = &enc_dev->mpp_dev;
+       mpp_dev->variant = rockchip_rkvepu2_get_drv_data(pdev);
+       ret = mpp_dev_common_probe(mpp_dev, pdev, &rkvepu_ops);
+       if (ret)
+               return ret;
+
+       ret = devm_request_threaded_irq(dev, mpp_dev->irq, NULL, mpp_rkvepu_isr,
+                                       IRQF_SHARED | IRQF_ONESHOT,
+                                       dev_name(dev), enc_dev);
+       if (ret) {
+               dev_err(dev, "register interrupter runtime failed\n");
+               return ret;
+       }
+
+       rockchip_mpp_rkvepu_assign_reset(enc_dev);
+
+       ret = mpp_dev_register_node(mpp_dev, mpp_dev->variant->node_name, NULL);
+       if (ret)
+               dev_err(dev, "register char device failed: %d\n", ret);
+
+       dev_info(dev, "probing finish\n");
+
+       platform_set_drvdata(pdev, enc_dev);
+
+       return 0;
+}
+
+static int rockchip_mpp_rkvepu_remove(struct platform_device *pdev)
+{
+       struct rockchip_rkvepu_dev *enc_dev = platform_get_drvdata(pdev);
+
+       mpp_dev_common_remove(&enc_dev->mpp_dev);
+
+       return 0;
+}
+
+static const struct of_device_id mpp_rkvepu2_dt_match[] = {
+       { .compatible = "rockchip,vpu-encoder-v2", .data = &rkvepu_v2_data},
+       {},
+};
+
+static void *rockchip_rkvepu2_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_rkvepu2_dt_match, pdev->dev.of_node);
+               if (match)
+                       driver_data = (struct mpp_dev_variant *)match->data;
+       }
+       return driver_data;
+}
+
+static struct platform_driver rockchip_rkvepu2_driver = {
+       .probe = rockchip_mpp_rkvepu_probe,
+       .remove = rockchip_mpp_rkvepu_remove,
+       .driver = {
+               .name = RKVEPU2_DRIVER_NAME,
+               .of_match_table = of_match_ptr(mpp_rkvepu2_dt_match),
+       },
+};
+
+static int __init mpp_dev_rkvepu2_init(void)
+{
+       int ret = platform_driver_register(&rockchip_rkvepu2_driver);
+
+       if (ret) {
+               mpp_err("Platform device register failed (%d).\n", ret);
+               return ret;
+       }
+
+       return ret;
+}
+
+static void __exit mpp_dev_rkvepu2_exit(void)
+{
+       platform_driver_unregister(&rockchip_rkvepu2_driver);
+}
+
+module_init(mpp_dev_rkvepu2_init);
+module_exit(mpp_dev_rkvepu2_exit);
+
+MODULE_DEVICE_TABLE(of, mpp_rkvepu2_dt_match);
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/staging/rockchip-mpp/mpp_iommu_dma.c 
b/drivers/staging/rockchip-mpp/mpp_iommu_dma.c
new file mode 100644
index 000000000000..2479b6c91bc9
--- /dev/null
+++ b/drivers/staging/rockchip-mpp/mpp_iommu_dma.c
@@ -0,0 +1,292 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2016 - 2017 Fuzhou Rockchip Electronics Co., Ltd
+ *             Randy Li, <ay...@soulik.info>
+ *
+ * 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/dma-buf.h>
+#include <linux/dma-iommu.h>
+#include <linux/iommu.h>
+#include <linux/kref.h>
+#include <linux/rcupdate.h>
+#include <linux/slab.h>
+
+#include "mpp_iommu_dma.h"
+
+/* pixel buffer, stream buffer and video codec buffer, not used */
+#define BUFFER_LIST_MAX_NUMS           30
+
+struct mpp_dma_buffer {
+       struct list_head list;
+       struct mpp_dma_session *session;
+       /* DMABUF information */
+       struct dma_buf *dma_buf;
+       struct dma_buf_attachment *attach;
+       struct sg_table *sgt;
+       enum dma_data_direction dir;
+
+       dma_addr_t iova;
+       unsigned long size;
+       /* Only be used for identifying the buffer */
+       int fd;
+
+       struct kref ref;
+       struct rcu_head rcu;
+};
+
+struct mpp_dma_session {
+       struct list_head buffer_list;
+       /* the mutex for the above buffer list */
+       struct mutex list_mutex;
+
+       struct device *dev;
+};
+
+static struct mpp_dma_buffer *
+mpp_dma_find_buffer(struct mpp_dma_session *session, int fd)
+{
+       struct mpp_dma_buffer *buffer = NULL;
+
+       list_for_each_entry_rcu(buffer, &session->buffer_list, list) {
+               /*
+                * As long as the last reference is hold by the buffer pool,
+                * the same fd won't be assigned to the other application.
+                */
+               if (buffer->fd == fd)
+                       return buffer;
+       }
+
+       return NULL;
+}
+
+/* Release the buffer from the current list */
+static void mpp_dma_buffer_delete_rcu(struct kref *ref)
+{
+       struct mpp_dma_buffer *buffer =
+               container_of(ref, struct mpp_dma_buffer, ref);
+
+       mutex_lock(&buffer->session->list_mutex);
+       list_del_rcu(&buffer->list);
+       mutex_unlock(&buffer->session->list_mutex);
+
+       dma_buf_unmap_attachment(buffer->attach, buffer->sgt, buffer->dir);
+       dma_buf_detach(buffer->dma_buf, buffer->attach);
+       dma_buf_put(buffer->dma_buf);
+       kfree_rcu(buffer, rcu);
+}
+
+int mpp_dma_release_fd(struct mpp_dma_session *session, int fd)
+{
+       struct device *dev = session->dev;
+       struct mpp_dma_buffer *buffer = NULL;
+
+       rcu_read_lock();
+       buffer = mpp_dma_find_buffer(session, fd);
+       rcu_read_unlock();
+       if (IS_ERR_OR_NULL(buffer)) {
+               dev_err(dev, "can not find %d buffer in list to release\n", fd);
+
+               return -EINVAL;
+       }
+
+       kref_put(&buffer->ref, mpp_dma_buffer_delete_rcu);
+
+       return 0;
+}
+
+dma_addr_t mpp_dma_import_fd(struct mpp_dma_session *session, int fd)
+{
+       struct mpp_dma_buffer *buffer = NULL;
+       struct dma_buf_attachment *attach;
+       struct sg_table *sgt;
+       struct dma_buf *dma_buf;
+       int ret = 0;
+
+       if (!session)
+               return -EINVAL;
+
+       dma_buf = dma_buf_get(fd);
+       if (IS_ERR(dma_buf)) {
+               ret = PTR_ERR(dma_buf);
+               return ret;
+       }
+
+       rcu_read_lock();
+       buffer = mpp_dma_find_buffer(session, fd);
+       if (!IS_ERR_OR_NULL(buffer)) {
+               if (buffer->dma_buf == dma_buf) {
+                       if (kref_get_unless_zero(&buffer->ref)) {
+                               dma_buf_put(dma_buf);
+                               rcu_read_unlock();
+                               return buffer->iova;
+                       }
+               }
+               rcu_read_unlock();
+               dev_dbg(session->dev,
+                       "missing the fd %d\n", fd);
+               kref_put(&buffer->ref, mpp_dma_buffer_delete_rcu);
+       } else {
+               rcu_read_unlock();
+       }
+
+       /* A new DMA buffer */
+       buffer = kzalloc(sizeof(*buffer), GFP_KERNEL);
+       if (!buffer) {
+               ret = -ENOMEM;
+               goto err;
+       }
+
+       buffer->dma_buf = dma_buf;
+       buffer->fd = fd;
+       /* TODO */
+       buffer->dir = DMA_BIDIRECTIONAL;
+
+       kref_init(&buffer->ref);
+
+       attach = dma_buf_attach(buffer->dma_buf, session->dev);
+       if (IS_ERR(attach)) {
+               ret = PTR_ERR(attach);
+               goto fail_out;
+       }
+
+       sgt = dma_buf_map_attachment(attach, buffer->dir);
+       if (IS_ERR(sgt)) {
+               ret = PTR_ERR(sgt);
+               goto fail_detach;
+       }
+
+       buffer->iova = sg_dma_address(sgt->sgl);
+       buffer->size = sg_dma_len(sgt->sgl);
+
+       buffer->attach = attach;
+       buffer->sgt = sgt;
+
+       /* Increase the reference for used outside the buffer pool */
+       kref_get(&buffer->ref);
+
+       INIT_LIST_HEAD(&buffer->list);
+
+       mutex_lock(&session->list_mutex);
+       buffer->session = session;
+       list_add_tail_rcu(&buffer->list, &session->buffer_list);
+       mutex_unlock(&session->list_mutex);
+
+       return buffer->iova;
+
+fail_detach:
+       dma_buf_detach(buffer->dma_buf, attach);
+fail_out:
+       kfree(buffer);
+err:
+       dma_buf_put(dma_buf);
+       return ret;
+}
+
+void mpp_dma_destroy_session(struct mpp_dma_session *session)
+{
+       struct mpp_dma_buffer *buffer = NULL;
+
+       if (!session)
+               return;
+
+       mutex_lock(&session->list_mutex);
+       list_for_each_entry_rcu(buffer, &session->buffer_list, list) {
+               list_del_rcu(&buffer->list);
+               dma_buf_unmap_attachment(buffer->attach, buffer->sgt,
+                                        buffer->dir);
+               dma_buf_detach(buffer->dma_buf, buffer->attach);
+               dma_buf_put(buffer->dma_buf);
+               kfree_rcu(buffer, rcu);
+       }
+       mutex_unlock(&session->list_mutex);
+
+       kfree(session);
+}
+
+struct mpp_dma_session *mpp_dma_session_create(struct device *dev)
+{
+       struct mpp_dma_session *session = NULL;
+
+       session = kzalloc(sizeof(*session), GFP_KERNEL);
+       if (!session)
+               return session;
+
+       INIT_LIST_HEAD(&session->buffer_list);
+       mutex_init(&session->list_mutex);
+
+       session->dev = dev;
+
+       return session;
+}
+
+void mpp_iommu_detach(struct mpp_iommu_info *info)
+{
+       struct iommu_domain *domain = info->domain;
+       struct iommu_group *group = info->group;
+
+       iommu_detach_group(domain, group);
+}
+
+int mpp_iommu_attach(struct mpp_iommu_info *info)
+{
+       struct iommu_domain *domain = info->domain;
+       struct iommu_group *group = info->group;
+       int ret;
+
+       ret = iommu_attach_group(domain, group);
+       if (ret)
+               return ret;
+
+       return 0;
+}
+
+struct mpp_iommu_info *mpp_iommu_probe(struct device *dev)
+{
+       struct mpp_iommu_info *info = NULL;
+       int ret = 0;
+
+       info = kzalloc(sizeof(*info), GFP_KERNEL);
+       if (!info) {
+               ret = -ENOMEM;
+               goto err;
+       }
+
+       info->group = iommu_group_get(dev);
+       if (!info->group) {
+               ret = -EINVAL;
+               goto err_free_info;
+       }
+
+       info->domain = iommu_get_domain_for_dev(dev);
+       if (!info->domain) {
+               ret = -EINVAL;
+               goto err_put_group;
+       }
+
+       return info;
+
+err_put_group:
+       iommu_group_put(info->group);
+err_free_info:
+       kfree(info);
+err:
+       return ERR_PTR(ret);
+}
+
+int mpp_iommu_remove(struct mpp_iommu_info *info)
+{
+       iommu_group_put(info->group);
+       kfree(info);
+
+       return 0;
+}
diff --git a/drivers/staging/rockchip-mpp/mpp_iommu_dma.h 
b/drivers/staging/rockchip-mpp/mpp_iommu_dma.h
new file mode 100644
index 000000000000..5c6866397daa
--- /dev/null
+++ b/drivers/staging/rockchip-mpp/mpp_iommu_dma.h
@@ -0,0 +1,42 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd
+ *             Randy Li, <ay...@soulik.info>
+ *
+ * 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 __VCODEC_IOMMU_DMA_H__
+#define __VCODEC_IOMMU_DMA_H__
+
+#include <linux/iommu.h>
+#include <linux/dma-mapping.h>
+
+struct mpp_iommu_info {
+       struct iommu_domain *domain;
+       struct iommu_group *group;
+};
+
+struct mpp_dma_session;
+
+struct mpp_dma_session *mpp_dma_session_create(struct device *dev);
+void mpp_dma_destroy_session(struct mpp_dma_session *session);
+
+dma_addr_t mpp_dma_import_fd(struct mpp_dma_session *session, int fd);
+int mpp_dma_release_fd(struct mpp_dma_session *session, int fd);
+
+struct mpp_iommu_info *mpp_iommu_probe(struct device *dev);
+int mpp_iommu_remove(struct mpp_iommu_info *info);
+
+int mpp_iommu_attach(struct mpp_iommu_info *info);
+void mpp_iommu_detach(struct mpp_iommu_info *info);
+
+#endif
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/include/uapi/video/rk_vpu_service.h 
b/include/uapi/video/rk_vpu_service.h
new file mode 100644
index 000000000000..b75e03c391c7
--- /dev/null
+++ b/include/uapi/video/rk_vpu_service.h
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2015 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 __UAPI_LINUX_RK_VPU_SERVICE_H__
+#define __UAPI_LINUX_RK_VPU_SERVICE_H__
+
+#include <linux/types.h>
+#include <asm/ioctl.h>
+
+/*
+ * Ioctl definitions
+ */
+
+/* Use 'l' as magic number */
+#define VPU_IOC_MAGIC                  'l'
+
+#define VPU_IOC_SET_CLIENT_TYPE                _IOW(VPU_IOC_MAGIC, 1, __u32)
+#define VPU_IOC_GET_HW_FUSE_STATUS     _IOW(VPU_IOC_MAGIC, 2, unsigned long)
+
+#define VPU_IOC_SET_REG                        _IOW(VPU_IOC_MAGIC, 3, unsigned 
long)
+#define VPU_IOC_GET_REG                        _IOW(VPU_IOC_MAGIC, 4, unsigned 
long)
+
+#define VPU_IOC_PROBE_IOMMU_STATUS     _IOR(VPU_IOC_MAGIC, 5, __u32)
+#define VPU_IOC_SET_DRIVER_DATA                _IOW(VPU_IOC_MAGIC, 64, u32)
+
+struct vpu_request {
+       __u32 *req;
+       __u32 size;
+};
+
+/* Hardware decoder configuration description */
+struct vpu_dec_config {
+       /* Maximum video decoding width supported  */
+       __u32 max_dec_pic_width;
+       /* Maximum output width of Post-Processor */
+       __u32 max_pp_out_pic_width;
+       /* HW supports h.264 */
+       __u32 h264_support;
+       /* HW supports JPEG */
+       __u32 jpeg_support;
+       /* HW supports MPEG-4 */
+       __u32 mpeg4_support;
+       /* HW supports custom MPEG-4 features */
+       __u32 custom_mpeg4_support;
+       /* HW supports VC-1 Simple */
+       __u32 vc1_support;
+       /* HW supports MPEG-2 */
+       __u32 mpeg2_support;
+       /* HW supports post-processor */
+       __u32 pp_support;
+       /* HW post-processor functions bitmask */
+       __u32 pp_config;
+       /* HW supports Sorenson Spark */
+       __u32 sorenson_support;
+       /* HW supports reference picture buffering */
+       __u32 ref_buf_support;
+       /* HW supports VP6 */
+       __u32 vp6_support;
+       /* HW supports VP7 */
+       __u32 vp7_support;
+       /* HW supports VP8 */
+       __u32 vp8_support;
+       /* HW supports AVS */
+       __u32 avs_support;
+       /* HW supports JPEG extensions */
+       __u32 jpeg_ext_support;
+       __u32 reserve;
+       /* HW supports H264 MVC extension */
+       __u32 mvc_support;
+};
+
+/* Hardware encoder configuration description */
+struct vpu_enc_config {
+       /* Maximum supported width for video encoding (not JPEG) */
+       __u32 max_encoded_width;
+       /* HW supports H.264 */
+       __u32 h264_enabled;
+       /* HW supports JPEG */
+       __u32 jpeg_enabled;
+       /* HW supports MPEG-4 */
+       __u32 mpeg4_enabled;
+       /* HW supports video stabilization */
+       __u32 vs_enabled;
+       /* HW supports RGB input */
+       __u32 rgb_enabled;
+       __u32 reg_size;
+       __u32 reserv[2];
+};
+
+#endif
-- 
2.20.1

Reply via email to