2019-01-05 18:34:16

by Randy Li

[permalink] [raw]
Subject: [PATCH 0/4] Rockchip: the vendor video codec for reference

Those patches are not for merging, I won't dream on it. As I said in
previous email, this driver is used for checking the status of the other
drivers. I have checked this driver would memory dump, its output looks
well. The reason I didn't use the video output is VOP driver doesn't
work well.

Also I want to offer a reference for those people who want to develop
the V4L2 request API driver for Rockchip and you can use this driver to
comparing the result as well. I should said either the one for sunxi nor
chromium have not reached the vendor production level.

I want to point out the obvious problem of the current V4L2 driver, I
think that is the one I developed years ago and refresh it in the last
year, then it is merged now.

That driver won't aware the parallel problem between the devices. The
video codec in Rockchip is very complex, the decoder and encoder are
NOT paired in some platforms. Also a decoder or encoder can share some
resource with the other decoder or encoder, requesting a mux in the GRF
device. We call those devices sharing resource a combo, they may or may
not having a individual IOMMU for each of its child devices. Also any of
those devies allowing support less codecs than its full state.

The RK3328 is a good example of that,
---------------------------------------------
| Video decoder for H264, VP8, JPEG, MPEG-4 Part 2
| Video decoder for AVS+
|--------------------------------------------
| Video decoder for H265, H265, VP9
|--------------------------------------------
| Video encoder for H265,
---------------------------------------------
| Video encoder for H264, JPEG
---------------------------------------------
that is why I rewrote the device tree files in these patches, the current
device tree is not suitable for the other platforms.

Besides, the V4L2 driver don't support the error correction and tolerance or
status recovery. That is more about the parser in the userspace but a
common parser won't be able to do that, different video codec vendor
would request a different method, also V4L2 request API is not suitable
to feedback status.

Anyway, the current developing for decoder is at lease acceptable for
those simple situations. But I think encoder would become a big issue,
lucky, it seems that nobody care about encoder. If you have ever looked
the video encoder for chromium, you would feel disgusted, it is not
Google's fault, just not suitable for V4L2.

I don't want to talk much about encoder here, basially the encoder is
much simpler than decoder requesting less dynamic settings, but those
dynamic settings request a more real time feedback or related.

I don't see much advantage the V4L2 brings, only the compatibility left.
The previous vendor driver would have DMA buffer problem but it is
solved in this one. Ndufresne told me about buffer fencing, but I don't
think it is useful only complication. That is why I didn't develop the
V4L2 for a long time.

I would attend the FOSDEM 2019, if you have any problem, I think you can
catch me easily there.

Randy Li (4):
staging: video: rockchip: video codec for vendor API
staging: video: rockchip: fixup for upstream
staging: video: rockchip: add video codec
arm64: dts: rockchip: add video codec for rk3399

.../boot/dts/rockchip/rk3399-sapphire.dtsi | 29 +
arch/arm64/boot/dts/rockchip/rk3399.dtsi | 68 +-
drivers/staging/Kconfig | 2 +
drivers/staging/Makefile | 1 +
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 | 855 +++++++++++++++
drivers/staging/rockchip-mpp/mpp_dev_vdpu1.c | 614 +++++++++++
drivers/staging/rockchip-mpp/mpp_dev_vdpu2.c | 576 +++++++++++
drivers/staging/rockchip-mpp/mpp_dev_vepu1.c | 480 +++++++++
drivers/staging/rockchip-mpp/mpp_dev_vepu2.c | 477 +++++++++
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 ++
19 files changed, 5110 insertions(+), 7 deletions(-)
create mode 100644 drivers/staging/rockchip-mpp/Kconfig
create mode 100644 drivers/staging/rockchip-mpp/Makefile
create mode 100644 drivers/staging/rockchip-mpp/mpp_debug.h
create mode 100644 drivers/staging/rockchip-mpp/mpp_dev_common.c
create mode 100644 drivers/staging/rockchip-mpp/mpp_dev_common.h
create mode 100644 drivers/staging/rockchip-mpp/mpp_dev_rkvdec.c
create mode 100644 drivers/staging/rockchip-mpp/mpp_dev_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

--
2.20.1



2019-01-05 18:33:41

by Randy Li

[permalink] [raw]
Subject: [PATCH 3/4] staging: video: rockchip: add video codec

Signed-off-by: Randy Li <[email protected]>
---
drivers/staging/Kconfig | 2 ++
drivers/staging/Makefile | 1 +
2 files changed, 3 insertions(+)

diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig
index e4f608815c05..81634dd0a283 100644
--- a/drivers/staging/Kconfig
+++ b/drivers/staging/Kconfig
@@ -122,4 +122,6 @@ source "drivers/staging/axis-fifo/Kconfig"

source "drivers/staging/erofs/Kconfig"

+source "drivers/staging/rockchip-mpp/Kconfig"
+
endif # STAGING
diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile
index 5868631e8f1b..22499c68c21e 100644
--- a/drivers/staging/Makefile
+++ b/drivers/staging/Makefile
@@ -51,3 +51,4 @@ obj-$(CONFIG_SOC_MT7621) += mt7621-dts/
obj-$(CONFIG_STAGING_GASKET_FRAMEWORK) += gasket/
obj-$(CONFIG_XIL_AXIS_FIFO) += axis-fifo/
obj-$(CONFIG_EROFS_FS) += erofs/
+obj-$(CONFIG_ROCKCHIP_MPP_SERVICE) += rockchip-mpp/
--
2.20.1


2019-01-05 18:33:41

by Randy Li

[permalink] [raw]
Subject: [PATCH 4/4] arm64: dts: rockchip: add video codec for rk3399

It offers an example how a full features video codec
should be configured.

The original clocks assignment don't look good, if the clocks
lower than 300MHZ, most of decoing tasks would suffer from
timeout problem, 500MHZ is also a little high for RK3399
running in a stable state.

Signed-off-by: Randy Li <[email protected]>
---
.../boot/dts/rockchip/rk3399-sapphire.dtsi | 29 ++++++++
arch/arm64/boot/dts/rockchip/rk3399.dtsi | 68 +++++++++++++++++--
2 files changed, 90 insertions(+), 7 deletions(-)

diff --git a/arch/arm64/boot/dts/rockchip/rk3399-sapphire.dtsi b/arch/arm64/boot/dts/rockchip/rk3399-sapphire.dtsi
index 946d3589575a..c3db878bae45 100644
--- a/arch/arm64/boot/dts/rockchip/rk3399-sapphire.dtsi
+++ b/arch/arm64/boot/dts/rockchip/rk3399-sapphire.dtsi
@@ -632,6 +632,35 @@
dr_mode = "host";
};

+&rkvdec {
+ status = "okay";
+};
+
+&rkvdec_srv {
+ status = "okay";
+};
+
+&vdec_mmu {
+ status = "okay";
+};
+
+&vdpu {
+ status = "okay";
+};
+
+&vepu {
+ status = "okay";
+};
+
+&vpu_service {
+ status = "okay";
+};
+
+&vpu_mmu {
+ status = "okay";
+
+};
+
&vopb {
status = "okay";
};
diff --git a/arch/arm64/boot/dts/rockchip/rk3399.dtsi b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
index b22b2e40422b..5fa3247e7bf0 100644
--- a/arch/arm64/boot/dts/rockchip/rk3399.dtsi
+++ b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
@@ -1242,16 +1242,39 @@
status = "disabled";
};

- vpu: video-codec@ff650000 {
- compatible = "rockchip,rk3399-vpu";
- reg = <0x0 0xff650000 0x0 0x800>;
- interrupts = <GIC_SPI 114 IRQ_TYPE_LEVEL_HIGH 0>,
- <GIC_SPI 113 IRQ_TYPE_LEVEL_HIGH 0>;
- interrupt-names = "vepu", "vdpu";
+ vpu_service: vpu-srv {
+ compatible = "rockchip,mpp-service";
+ status = "disabled";
+ };
+
+ vepu: vpu-encoder@ff650000 {
+ compatible = "rockchip,vpu-encoder-v2";
+ reg = <0x0 0xff650000 0x0 0x400>;
+ interrupts = <GIC_SPI 114 IRQ_TYPE_LEVEL_HIGH 0>;
+ interrupt-names = "irq_enc";
clocks = <&cru ACLK_VCODEC>, <&cru HCLK_VCODEC>;
- clock-names = "aclk", "hclk";
+ clock-names = "aclk_vcodec", "hclk_vcodec";
+ resets = <&cru SRST_H_VCODEC>, <&cru SRST_A_VCODEC>;
+ reset-names = "video_h", "video_a";
iommus = <&vpu_mmu>;
power-domains = <&power RK3399_PD_VCODEC>;
+ rockchip,srv = <&vpu_service>;
+ status = "disabled";
+ };
+
+ vdpu: vpu-decoder@ff650400 {
+ compatible = "rockchip,vpu-decoder-v2";
+ reg = <0x0 0xff650400 0x0 0x400>;
+ interrupts = <GIC_SPI 113 IRQ_TYPE_LEVEL_HIGH 0>;
+ interrupt-names = "irq_dec";
+ iommus = <&vpu_mmu>;
+ clocks = <&cru ACLK_VCODEC>, <&cru HCLK_VCODEC>;
+ clock-names = "aclk_vcodec", "hclk_vcodec";
+ resets = <&cru SRST_H_VCODEC>, <&cru SRST_A_VCODEC>;
+ reset-names = "video_h", "video_a";
+ power-domains = <&power RK3399_PD_VCODEC>;
+ rockchip,srv = <&vpu_service>;
+ status = "disabled";
};

vpu_mmu: iommu@ff650800 {
@@ -1261,11 +1284,42 @@
interrupt-names = "vpu_mmu";
clocks = <&cru ACLK_VCODEC>, <&cru HCLK_VCODEC>;
clock-names = "aclk", "iface";
+ assigned-clocks = <&cru ACLK_VCODEC_PRE>;
+ assigned-clock-parents = <&cru PLL_GPLL>;
#iommu-cells = <0>;
power-domains = <&power RK3399_PD_VCODEC>;
status = "disabled";
};

+ rkvdec_srv: rkvdec-srv {
+ compatible = "rockchip,mpp-service";
+ status = "disabled";
+ };
+
+ rkvdec: video-decoder@ff660000 {
+ compatible = "rockchip,video-decoder-v1";
+ reg = <0x0 0xff660000 0x0 0x400>;
+ interrupts = <GIC_SPI 116 IRQ_TYPE_LEVEL_HIGH 0>;
+ interrupt-names = "irq_dec";
+ clocks = <&cru ACLK_VDU>, <&cru HCLK_VDU>,
+ <&cru SCLK_VDU_CA>, <&cru SCLK_VDU_CORE>;
+ clock-names = "aclk_vcodec", "hclk_vcodec",
+ "clk_cabac", "clk_core";
+ assigned-clocks = <&cru ACLK_VDU_PRE>, <&cru SCLK_VDU_CA>,
+ <&cru SCLK_VDU_CORE>;
+ assigned-clock-parents = <&cru PLL_NPLL>, <&cru PLL_NPLL>,
+ <&cru PLL_NPLL>;
+ resets = <&cru SRST_H_VDU>, <&cru SRST_A_VDU>,
+ <&cru SRST_VDU_CORE>, <&cru SRST_VDU_CA>,
+ <&cru SRST_A_VDU_NOC>, <&cru SRST_H_VDU_NOC>;
+ reset-names = "video_h", "video_a", "video_core", "video_cabac",
+ "niu_a", "niu_h";
+ power-domains = <&power RK3399_PD_VDU>;
+ rockchip,srv = <&rkvdec_srv>;
+ iommus = <&vdec_mmu>;
+ status = "disabled";
+ };
+
vdec_mmu: iommu@ff660480 {
compatible = "rockchip,iommu";
reg = <0x0 0xff660480 0x0 0x40>, <0x0 0xff6604c0 0x0 0x40>;
--
2.20.1


2019-01-05 18:34:38

by Randy Li

[permalink] [raw]
Subject: [PATCH 2/4] staging: video: rockchip: fixup for upstream

Fixing those deprecated function from vendor kernel.
Removing those features don't exist in upstream kernel.

Signed-off-by: Randy Li <[email protected]>
---
drivers/staging/rockchip-mpp/mpp_dev_common.c | 12 ++++++------
drivers/staging/rockchip-mpp/mpp_dev_common.h | 2 +-
drivers/staging/rockchip-mpp/mpp_dev_rkvdec.c | 7 +++----
drivers/staging/rockchip-mpp/mpp_dev_vdpu1.c | 5 ++---
drivers/staging/rockchip-mpp/mpp_dev_vdpu2.c | 5 ++---
drivers/staging/rockchip-mpp/mpp_dev_vepu1.c | 5 ++---
drivers/staging/rockchip-mpp/mpp_dev_vepu2.c | 5 ++---
7 files changed, 18 insertions(+), 23 deletions(-)

diff --git a/drivers/staging/rockchip-mpp/mpp_dev_common.c b/drivers/staging/rockchip-mpp/mpp_dev_common.c
index 159aa5d244ce..9a257c254d4d 100644
--- a/drivers/staging/rockchip-mpp/mpp_dev_common.c
+++ b/drivers/staging/rockchip-mpp/mpp_dev_common.c
@@ -937,18 +937,18 @@ EXPORT_SYMBOL(mpp_dev_read);
void mpp_debug_time_record(struct mpp_task *task)
{
if (unlikely(debug & DEBUG_TIMING) && task)
- do_gettimeofday(&task->start);
+ getboottime64(&task->start);
}
EXPORT_SYMBOL(mpp_debug_time_record);

void mpp_debug_time_diff(struct mpp_task *task)
{
- struct timeval end;
+ struct timespec64 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));
+ getboottime64(&end);
+ mpp_debug(DEBUG_TIMING, "time: %lld ms\n",
+ (end.tv_sec - task->start.tv_sec) * 1000 +
+ (end.tv_nsec - task->start.tv_nsec) / 1000000);
}
EXPORT_SYMBOL(mpp_debug_time_diff);

diff --git a/drivers/staging/rockchip-mpp/mpp_dev_common.h b/drivers/staging/rockchip-mpp/mpp_dev_common.h
index 8a7dc7444dc3..76d53ec2c452 100644
--- a/drivers/staging/rockchip-mpp/mpp_dev_common.h
+++ b/drivers/staging/rockchip-mpp/mpp_dev_common.h
@@ -124,7 +124,7 @@ struct mpp_task {
struct work_struct work;

/* record context running start time */
- struct timeval start;
+ struct timespec64 start;
};

/*
diff --git a/drivers/staging/rockchip-mpp/mpp_dev_rkvdec.c b/drivers/staging/rockchip-mpp/mpp_dev_rkvdec.c
index a3da27cfc10e..44d76eba901f 100644
--- a/drivers/staging/rockchip-mpp/mpp_dev_rkvdec.c
+++ b/drivers/staging/rockchip-mpp/mpp_dev_rkvdec.c
@@ -19,10 +19,10 @@
#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 <soc/rockchip/rockchip_sip.h>

#include "mpp_debug.h"
#include "mpp_dev_common.h"
@@ -230,8 +230,7 @@ static int fill_scaling_list_pps(struct rkvdec_task *task, int fd, int offset,
return -ENOENT;
}

- ret = dma_buf_begin_cpu_access(dmabuf, 0, dmabuf->size,
- DMA_FROM_DEVICE);
+ ret = dma_buf_begin_cpu_access(dmabuf, DMA_FROM_DEVICE);
if (ret) {
dev_err(dev, "can't access the pps buffer\n");
goto done;
@@ -277,7 +276,7 @@ static int fill_scaling_list_pps(struct rkvdec_task *task, int fd, int offset,

done:
dma_buf_vunmap(dmabuf, vaddr);
- dma_buf_end_cpu_access(dmabuf, 0, dmabuf->size, DMA_FROM_DEVICE);
+ dma_buf_end_cpu_access(dmabuf, DMA_FROM_DEVICE);
dma_buf_put(dmabuf);

return ret;
diff --git a/drivers/staging/rockchip-mpp/mpp_dev_vdpu1.c b/drivers/staging/rockchip-mpp/mpp_dev_vdpu1.c
index 4371a1a6080b..63ffd79bfe83 100644
--- a/drivers/staging/rockchip-mpp/mpp_dev_vdpu1.c
+++ b/drivers/staging/rockchip-mpp/mpp_dev_vdpu1.c
@@ -460,9 +460,8 @@ 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");
+ dec_dev->rst_a = devm_reset_control_get_shared(mpp_dev->dev, "video_a");
+ dec_dev->rst_h = devm_reset_control_get_shared(mpp_dev->dev, "video_h");

if (IS_ERR_OR_NULL(dec_dev->rst_a)) {
mpp_err("No aclk reset resource define\n");
diff --git a/drivers/staging/rockchip-mpp/mpp_dev_vdpu2.c b/drivers/staging/rockchip-mpp/mpp_dev_vdpu2.c
index b131790f72a3..5789c8940543 100644
--- a/drivers/staging/rockchip-mpp/mpp_dev_vdpu2.c
+++ b/drivers/staging/rockchip-mpp/mpp_dev_vdpu2.c
@@ -428,9 +428,8 @@ 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");
+ dec_dev->rst_a = devm_reset_control_get_shared(mpp_dev->dev, "video_a");
+ dec_dev->rst_h = devm_reset_control_get_shared(mpp_dev->dev, "video_h");

if (IS_ERR_OR_NULL(dec_dev->rst_a)) {
mpp_err("No aclk reset resource define\n");
diff --git a/drivers/staging/rockchip-mpp/mpp_dev_vepu1.c b/drivers/staging/rockchip-mpp/mpp_dev_vepu1.c
index 64619092c792..ebe8174e931e 100644
--- a/drivers/staging/rockchip-mpp/mpp_dev_vepu1.c
+++ b/drivers/staging/rockchip-mpp/mpp_dev_vepu1.c
@@ -330,9 +330,8 @@ 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");
+ enc_dev->rst_a = devm_reset_control_get_shared(mpp_dev->dev, "video_a");
+ enc_dev->rst_h = devm_reset_control_get_shared(mpp_dev->dev, "video_h");

if (IS_ERR_OR_NULL(enc_dev->rst_a)) {
mpp_err("No aclk reset resource define\n");
diff --git a/drivers/staging/rockchip-mpp/mpp_dev_vepu2.c b/drivers/staging/rockchip-mpp/mpp_dev_vepu2.c
index 48ec401145d5..889aaccf25c5 100644
--- a/drivers/staging/rockchip-mpp/mpp_dev_vepu2.c
+++ b/drivers/staging/rockchip-mpp/mpp_dev_vepu2.c
@@ -327,9 +327,8 @@ 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");
+ enc_dev->rst_a = devm_reset_control_get_shared(mpp_dev->dev, "video_a");
+ enc_dev->rst_h = devm_reset_control_get_shared(mpp_dev->dev, "video_h");

if (IS_ERR_OR_NULL(enc_dev->rst_a)) {
mpp_err("No aclk reset resource define\n");
--
2.20.1


2019-01-05 18:34:38

by Randy Li

[permalink] [raw]
Subject: [PATCH 1/4] staging: video: rockchip: video codec for vendor API

Signed-off-by: Randy Li <[email protected]>
---
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, <[email protected]>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/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, <[email protected]>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <asm/cacheflush.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/of_platform.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <soc/rockchip/pm_domains.h>
+
+#include "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, <[email protected]>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <asm/cacheflush.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/of_platform.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <soc/rockchip/pm_domains.h>
+
+#include "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, <[email protected]>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <asm/cacheflush.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/of_platform.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <soc/rockchip/pm_domains.h>
+
+#include "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, <[email protected]>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <asm/cacheflush.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/of_platform.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <soc/rockchip/pm_domains.h>
+
+#include "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, <[email protected]>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/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, <[email protected]>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef __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


2019-01-06 14:24:52

by Ezequiel Garcia

[permalink] [raw]
Subject: Re: [PATCH 4/4] arm64: dts: rockchip: add video codec for rk3399

Hi Randy,

Thanks a lot for this patches. They are really useful
to provide more insight into the VPU hardware.

This change will make the vpu encoder and vpu decoder
completely independent, can they really work in parallel?

Could you provide more details about what is
shared between these devices?

Thanks a lot!

On Sun, 2019-01-06 at 02:31 +0800, Randy Li wrote:
> It offers an example how a full features video codec
> should be configured.
>
> The original clocks assignment don't look good, if the clocks
> lower than 300MHZ, most of decoing tasks would suffer from
> timeout problem, 500MHZ is also a little high for RK3399
> running in a stable state.
>
> Signed-off-by: Randy Li <[email protected]>
> ---
> .../boot/dts/rockchip/rk3399-sapphire.dtsi | 29 ++++++++
> arch/arm64/boot/dts/rockchip/rk3399.dtsi | 68 +++++++++++++++++--
> 2 files changed, 90 insertions(+), 7 deletions(-)
>
> diff --git a/arch/arm64/boot/dts/rockchip/rk3399-sapphire.dtsi b/arch/arm64/boot/dts/rockchip/rk3399-sapphire.dtsi
> index 946d3589575a..c3db878bae45 100644
> --- a/arch/arm64/boot/dts/rockchip/rk3399-sapphire.dtsi
> +++ b/arch/arm64/boot/dts/rockchip/rk3399-sapphire.dtsi
> @@ -632,6 +632,35 @@
> dr_mode = "host";
> };
>
> +&rkvdec {
> + status = "okay";
> +};
> +
> +&rkvdec_srv {
> + status = "okay";
> +};
> +
> +&vdec_mmu {
> + status = "okay";
> +};
> +
> +&vdpu {
> + status = "okay";
> +};
> +
> +&vepu {
> + status = "okay";
> +};
> +
> +&vpu_service {
> + status = "okay";
> +};
> +
> +&vpu_mmu {
> + status = "okay";
> +
> +};
> +
> &vopb {
> status = "okay";
> };
> diff --git a/arch/arm64/boot/dts/rockchip/rk3399.dtsi b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
> index b22b2e40422b..5fa3247e7bf0 100644
> --- a/arch/arm64/boot/dts/rockchip/rk3399.dtsi
> +++ b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
> @@ -1242,16 +1242,39 @@
> status = "disabled";
> };
>
> - vpu: video-codec@ff650000 {
> - compatible = "rockchip,rk3399-vpu";
> - reg = <0x0 0xff650000 0x0 0x800>;
> - interrupts = <GIC_SPI 114 IRQ_TYPE_LEVEL_HIGH 0>,
> - <GIC_SPI 113 IRQ_TYPE_LEVEL_HIGH 0>;
> - interrupt-names = "vepu", "vdpu";
> + vpu_service: vpu-srv {
> + compatible = "rockchip,mpp-service";
> + status = "disabled";
> + };
> +
> + vepu: vpu-encoder@ff650000 {
> + compatible = "rockchip,vpu-encoder-v2";
> + reg = <0x0 0xff650000 0x0 0x400>;
> + interrupts = <GIC_SPI 114 IRQ_TYPE_LEVEL_HIGH 0>;
> + interrupt-names = "irq_enc";
> clocks = <&cru ACLK_VCODEC>, <&cru HCLK_VCODEC>;
> - clock-names = "aclk", "hclk";
> + clock-names = "aclk_vcodec", "hclk_vcodec";
> + resets = <&cru SRST_H_VCODEC>, <&cru SRST_A_VCODEC>;
> + reset-names = "video_h", "video_a";
> iommus = <&vpu_mmu>;
> power-domains = <&power RK3399_PD_VCODEC>;
> + rockchip,srv = <&vpu_service>;
> + status = "disabled";
> + };
> +
> + vdpu: vpu-decoder@ff650400 {
> + compatible = "rockchip,vpu-decoder-v2";
> + reg = <0x0 0xff650400 0x0 0x400>;
> + interrupts = <GIC_SPI 113 IRQ_TYPE_LEVEL_HIGH 0>;
> + interrupt-names = "irq_dec";
> + iommus = <&vpu_mmu>;
> + clocks = <&cru ACLK_VCODEC>, <&cru HCLK_VCODEC>;
> + clock-names = "aclk_vcodec", "hclk_vcodec";
> + resets = <&cru SRST_H_VCODEC>, <&cru SRST_A_VCODEC>;
> + reset-names = "video_h", "video_a";
> + power-domains = <&power RK3399_PD_VCODEC>;
> + rockchip,srv = <&vpu_service>;
> + status = "disabled";
> };
>
> vpu_mmu: iommu@ff650800 {
> @@ -1261,11 +1284,42 @@
> interrupt-names = "vpu_mmu";
> clocks = <&cru ACLK_VCODEC>, <&cru HCLK_VCODEC>;
> clock-names = "aclk", "iface";
> + assigned-clocks = <&cru ACLK_VCODEC_PRE>;
> + assigned-clock-parents = <&cru PLL_GPLL>;
> #iommu-cells = <0>;
> power-domains = <&power RK3399_PD_VCODEC>;
> status = "disabled";
> };
>
> + rkvdec_srv: rkvdec-srv {
> + compatible = "rockchip,mpp-service";
> + status = "disabled";
> + };
> +
> + rkvdec: video-decoder@ff660000 {
> + compatible = "rockchip,video-decoder-v1";
> + reg = <0x0 0xff660000 0x0 0x400>;
> + interrupts = <GIC_SPI 116 IRQ_TYPE_LEVEL_HIGH 0>;
> + interrupt-names = "irq_dec";
> + clocks = <&cru ACLK_VDU>, <&cru HCLK_VDU>,
> + <&cru SCLK_VDU_CA>, <&cru SCLK_VDU_CORE>;
> + clock-names = "aclk_vcodec", "hclk_vcodec",
> + "clk_cabac", "clk_core";
> + assigned-clocks = <&cru ACLK_VDU_PRE>, <&cru SCLK_VDU_CA>,
> + <&cru SCLK_VDU_CORE>;
> + assigned-clock-parents = <&cru PLL_NPLL>, <&cru PLL_NPLL>,
> + <&cru PLL_NPLL>;
> + resets = <&cru SRST_H_VDU>, <&cru SRST_A_VDU>,
> + <&cru SRST_VDU_CORE>, <&cru SRST_VDU_CA>,
> + <&cru SRST_A_VDU_NOC>, <&cru SRST_H_VDU_NOC>;
> + reset-names = "video_h", "video_a", "video_core", "video_cabac",
> + "niu_a", "niu_h";
> + power-domains = <&power RK3399_PD_VDU>;
> + rockchip,srv = <&rkvdec_srv>;
> + iommus = <&vdec_mmu>;
> + status = "disabled";
> + };
> +
> vdec_mmu: iommu@ff660480 {
> compatible = "rockchip,iommu";
> reg = <0x0 0xff660480 0x0 0x40>, <0x0 0xff6604c0 0x0 0x40>;



2019-01-06 15:08:30

by Randy Li

[permalink] [raw]
Subject: Re: [PATCH 4/4] arm64: dts: rockchip: add video codec for rk3399


> On Jan 6, 2019, at 10:22 PM, Ezequiel Garcia <[email protected]> wrote:
>
> Hi Randy,
>
> Thanks a lot for this patches. They are really useful
> to provide more insight into the VPU hardware.
>
> This change will make the vpu encoder and vpu decoder
> completely independent, can they really work in parallel?
As I said it depends on the platform, but with this patch, the user space would think they can work at the same time. BTW, not only the decoder, there is a post processor with the decoder, it can be used as part of the decoder pipeline with only a macro block delay or process the data from an external buffer.
I forget to write a note on what this driver doesn’t present. The real one would have much complex scheduler system, but this one is just a queue. More task management feature are not there.
Also the clock boosting is removing and the loading analysis, which are useful for encoder, especially on the rv1108.
> Could you provide more details about what is
> shared between these devices?
No, if Rockchip doesn’t tell, my mouth is sealed.
> Thanks a lot!
>
>> On Sun, 2019-01-06 at 02:31 +0800, Randy Li wrote:
>> It offers an example how a full features video codec
>> should be configured.
>>
>> The original clocks assignment don't look good, if the clocks
>> lower than 300MHZ, most of decoing tasks would suffer from
>> timeout problem, 500MHZ is also a little high for RK3399
>> running in a stable state.
>>
>> Signed-off-by: Randy Li <[email protected]>
>> ---
>> .../boot/dts/rockchip/rk3399-sapphire.dtsi | 29 ++++++++
>> arch/arm64/boot/dts/rockchip/rk3399.dtsi | 68 +++++++++++++++++--
>> 2 files changed, 90 insertions(+), 7 deletions(-)
>>
>> diff --git a/arch/arm64/boot/dts/rockchip/rk3399-sapphire.dtsi b/arch/arm64/boot/dts/rockchip/rk3399-sapphire.dtsi
>> index 946d3589575a..c3db878bae45 100644
>> --- a/arch/arm64/boot/dts/rockchip/rk3399-sapphire.dtsi
>> +++ b/arch/arm64/boot/dts/rockchip/rk3399-sapphire.dtsi
>> @@ -632,6 +632,35 @@
>> dr_mode = "host";
>> };
>>
>> +&rkvdec {
>> + status = "okay";
>> +};
>> +
>> +&rkvdec_srv {
>> + status = "okay";
>> +};
>> +
>> +&vdec_mmu {
>> + status = "okay";
>> +};
>> +
>> +&vdpu {
>> + status = "okay";
>> +};
>> +
>> +&vepu {
>> + status = "okay";
>> +};
>> +
>> +&vpu_service {
>> + status = "okay";
>> +};
>> +
>> +&vpu_mmu {
>> + status = "okay";
>> +
>> +};
>> +
>> &vopb {
>> status = "okay";
>> };
>> diff --git a/arch/arm64/boot/dts/rockchip/rk3399.dtsi b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
>> index b22b2e40422b..5fa3247e7bf0 100644
>> --- a/arch/arm64/boot/dts/rockchip/rk3399.dtsi
>> +++ b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
>> @@ -1242,16 +1242,39 @@
>> status = "disabled";
>> };
>>
>> - vpu: video-codec@ff650000 {
>> - compatible = "rockchip,rk3399-vpu";
>> - reg = <0x0 0xff650000 0x0 0x800>;
>> - interrupts = <GIC_SPI 114 IRQ_TYPE_LEVEL_HIGH 0>,
>> - <GIC_SPI 113 IRQ_TYPE_LEVEL_HIGH 0>;
>> - interrupt-names = "vepu", "vdpu";
>> + vpu_service: vpu-srv {
>> + compatible = "rockchip,mpp-service";
>> + status = "disabled";
>> + };
>> +
>> + vepu: vpu-encoder@ff650000 {
>> + compatible = "rockchip,vpu-encoder-v2";
>> + reg = <0x0 0xff650000 0x0 0x400>;
>> + interrupts = <GIC_SPI 114 IRQ_TYPE_LEVEL_HIGH 0>;
>> + interrupt-names = "irq_enc";
>> clocks = <&cru ACLK_VCODEC>, <&cru HCLK_VCODEC>;
>> - clock-names = "aclk", "hclk";
>> + clock-names = "aclk_vcodec", "hclk_vcodec";
>> + resets = <&cru SRST_H_VCODEC>, <&cru SRST_A_VCODEC>;
>> + reset-names = "video_h", "video_a";
>> iommus = <&vpu_mmu>;
>> power-domains = <&power RK3399_PD_VCODEC>;
>> + rockchip,srv = <&vpu_service>;
>> + status = "disabled";
>> + };
>> +
>> + vdpu: vpu-decoder@ff650400 {
>> + compatible = "rockchip,vpu-decoder-v2";
>> + reg = <0x0 0xff650400 0x0 0x400>;
>> + interrupts = <GIC_SPI 113 IRQ_TYPE_LEVEL_HIGH 0>;
>> + interrupt-names = "irq_dec";
>> + iommus = <&vpu_mmu>;
>> + clocks = <&cru ACLK_VCODEC>, <&cru HCLK_VCODEC>;
>> + clock-names = "aclk_vcodec", "hclk_vcodec";
>> + resets = <&cru SRST_H_VCODEC>, <&cru SRST_A_VCODEC>;
>> + reset-names = "video_h", "video_a";
>> + power-domains = <&power RK3399_PD_VCODEC>;
>> + rockchip,srv = <&vpu_service>;
>> + status = "disabled";
>> };
>>
>> vpu_mmu: iommu@ff650800 {
>> @@ -1261,11 +1284,42 @@
>> interrupt-names = "vpu_mmu";
>> clocks = <&cru ACLK_VCODEC>, <&cru HCLK_VCODEC>;
>> clock-names = "aclk", "iface";
>> + assigned-clocks = <&cru ACLK_VCODEC_PRE>;
>> + assigned-clock-parents = <&cru PLL_GPLL>;
>> #iommu-cells = <0>;
>> power-domains = <&power RK3399_PD_VCODEC>;
>> status = "disabled";
>> };
>>
>> + rkvdec_srv: rkvdec-srv {
>> + compatible = "rockchip,mpp-service";
>> + status = "disabled";
>> + };
>> +
>> + rkvdec: video-decoder@ff660000 {
>> + compatible = "rockchip,video-decoder-v1";
>> + reg = <0x0 0xff660000 0x0 0x400>;
>> + interrupts = <GIC_SPI 116 IRQ_TYPE_LEVEL_HIGH 0>;
>> + interrupt-names = "irq_dec";
>> + clocks = <&cru ACLK_VDU>, <&cru HCLK_VDU>,
>> + <&cru SCLK_VDU_CA>, <&cru SCLK_VDU_CORE>;
>> + clock-names = "aclk_vcodec", "hclk_vcodec",
>> + "clk_cabac", "clk_core";
>> + assigned-clocks = <&cru ACLK_VDU_PRE>, <&cru SCLK_VDU_CA>,
>> + <&cru SCLK_VDU_CORE>;
>> + assigned-clock-parents = <&cru PLL_NPLL>, <&cru PLL_NPLL>,
>> + <&cru PLL_NPLL>;
>> + resets = <&cru SRST_H_VDU>, <&cru SRST_A_VDU>,
>> + <&cru SRST_VDU_CORE>, <&cru SRST_VDU_CA>,
>> + <&cru SRST_A_VDU_NOC>, <&cru SRST_H_VDU_NOC>;
>> + reset-names = "video_h", "video_a", "video_core", "video_cabac",
>> + "niu_a", "niu_h";
>> + power-domains = <&power RK3399_PD_VDU>;
>> + rockchip,srv = <&rkvdec_srv>;
>> + iommus = <&vdec_mmu>;
>> + status = "disabled";
>> + };
>> +
>> vdec_mmu: iommu@ff660480 {
>> compatible = "rockchip,iommu";
>> reg = <0x0 0xff660480 0x0 0x40>, <0x0 0xff6604c0 0x0 0x40>;
>
>


2019-01-06 15:08:51

by Randy Li

[permalink] [raw]
Subject: Re: [PATCH 4/4] arm64: dts: rockchip: add video codec for rk3399


> On Jan 6, 2019, at 10:22 PM, Ezequiel Garcia <[email protected]> wrote:
>
> Hi Randy,
>
> Thanks a lot for this patches. They are really useful
> to provide more insight into the VPU hardware.
>
> This change will make the vpu encoder and vpu decoder
> completely independent, can they really work in parallel?
As I said it depends on the platform, but with this patch, the user space would think they can work at the same time. BTW, not only the decoder, there is a post processor with the decoder, it can be used as part of the decoder pipeline with only a macro block delay or process the data from an external buffer.
I forget to write a note on what this driver doesn’t present. The real one would have much complex scheduler system, but this one is just a queue. More task management feature are not there.
Also the clock boosting is removing and the loading analysis, which are useful for encoder, especially on the rv1108.
> Could you provide more details about what is
> shared between these devices?
No, if Rockchip doesn’t tell, my mouth is sealed.
> Thanks a lot!
>
>> On Sun, 2019-01-06 at 02:31 +0800, Randy Li wrote:
>> It offers an example how a full features video codec
>> should be configured.
>>
>> The original clocks assignment don't look good, if the clocks
>> lower than 300MHZ, most of decoing tasks would suffer from
>> timeout problem, 500MHZ is also a little high for RK3399
>> running in a stable state.
>>
>> Signed-off-by: Randy Li <[email protected]>
>> ---
>> .../boot/dts/rockchip/rk3399-sapphire.dtsi | 29 ++++++++
>> arch/arm64/boot/dts/rockchip/rk3399.dtsi | 68 +++++++++++++++++--
>> 2 files changed, 90 insertions(+), 7 deletions(-)
>>
>> diff --git a/arch/arm64/boot/dts/rockchip/rk3399-sapphire.dtsi b/arch/arm64/boot/dts/rockchip/rk3399-sapphire.dtsi
>> index 946d3589575a..c3db878bae45 100644
>> --- a/arch/arm64/boot/dts/rockchip/rk3399-sapphire.dtsi
>> +++ b/arch/arm64/boot/dts/rockchip/rk3399-sapphire.dtsi
>> @@ -632,6 +632,35 @@
>> dr_mode = "host";
>> };
>>
>> +&rkvdec {
>> + status = "okay";
>> +};
>> +
>> +&rkvdec_srv {
>> + status = "okay";
>> +};
>> +
>> +&vdec_mmu {
>> + status = "okay";
>> +};
>> +
>> +&vdpu {
>> + status = "okay";
>> +};
>> +
>> +&vepu {
>> + status = "okay";
>> +};
>> +
>> +&vpu_service {
>> + status = "okay";
>> +};
>> +
>> +&vpu_mmu {
>> + status = "okay";
>> +
>> +};
>> +
>> &vopb {
>> status = "okay";
>> };
>> diff --git a/arch/arm64/boot/dts/rockchip/rk3399.dtsi b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
>> index b22b2e40422b..5fa3247e7bf0 100644
>> --- a/arch/arm64/boot/dts/rockchip/rk3399.dtsi
>> +++ b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
>> @@ -1242,16 +1242,39 @@
>> status = "disabled";
>> };
>>
>> - vpu: video-codec@ff650000 {
>> - compatible = "rockchip,rk3399-vpu";
>> - reg = <0x0 0xff650000 0x0 0x800>;
>> - interrupts = <GIC_SPI 114 IRQ_TYPE_LEVEL_HIGH 0>,
>> - <GIC_SPI 113 IRQ_TYPE_LEVEL_HIGH 0>;
>> - interrupt-names = "vepu", "vdpu";
>> + vpu_service: vpu-srv {
>> + compatible = "rockchip,mpp-service";
>> + status = "disabled";
>> + };
>> +
>> + vepu: vpu-encoder@ff650000 {
>> + compatible = "rockchip,vpu-encoder-v2";
>> + reg = <0x0 0xff650000 0x0 0x400>;
>> + interrupts = <GIC_SPI 114 IRQ_TYPE_LEVEL_HIGH 0>;
>> + interrupt-names = "irq_enc";
>> clocks = <&cru ACLK_VCODEC>, <&cru HCLK_VCODEC>;
>> - clock-names = "aclk", "hclk";
>> + clock-names = "aclk_vcodec", "hclk_vcodec";
>> + resets = <&cru SRST_H_VCODEC>, <&cru SRST_A_VCODEC>;
>> + reset-names = "video_h", "video_a";
>> iommus = <&vpu_mmu>;
>> power-domains = <&power RK3399_PD_VCODEC>;
>> + rockchip,srv = <&vpu_service>;
>> + status = "disabled";
>> + };
>> +
>> + vdpu: vpu-decoder@ff650400 {
>> + compatible = "rockchip,vpu-decoder-v2";
>> + reg = <0x0 0xff650400 0x0 0x400>;
>> + interrupts = <GIC_SPI 113 IRQ_TYPE_LEVEL_HIGH 0>;
>> + interrupt-names = "irq_dec";
>> + iommus = <&vpu_mmu>;
>> + clocks = <&cru ACLK_VCODEC>, <&cru HCLK_VCODEC>;
>> + clock-names = "aclk_vcodec", "hclk_vcodec";
>> + resets = <&cru SRST_H_VCODEC>, <&cru SRST_A_VCODEC>;
>> + reset-names = "video_h", "video_a";
>> + power-domains = <&power RK3399_PD_VCODEC>;
>> + rockchip,srv = <&vpu_service>;
>> + status = "disabled";
>> };
>>
>> vpu_mmu: iommu@ff650800 {
>> @@ -1261,11 +1284,42 @@
>> interrupt-names = "vpu_mmu";
>> clocks = <&cru ACLK_VCODEC>, <&cru HCLK_VCODEC>;
>> clock-names = "aclk", "iface";
>> + assigned-clocks = <&cru ACLK_VCODEC_PRE>;
>> + assigned-clock-parents = <&cru PLL_GPLL>;
>> #iommu-cells = <0>;
>> power-domains = <&power RK3399_PD_VCODEC>;
>> status = "disabled";
>> };
>>
>> + rkvdec_srv: rkvdec-srv {
>> + compatible = "rockchip,mpp-service";
>> + status = "disabled";
>> + };
>> +
>> + rkvdec: video-decoder@ff660000 {
>> + compatible = "rockchip,video-decoder-v1";
>> + reg = <0x0 0xff660000 0x0 0x400>;
>> + interrupts = <GIC_SPI 116 IRQ_TYPE_LEVEL_HIGH 0>;
>> + interrupt-names = "irq_dec";
>> + clocks = <&cru ACLK_VDU>, <&cru HCLK_VDU>,
>> + <&cru SCLK_VDU_CA>, <&cru SCLK_VDU_CORE>;
>> + clock-names = "aclk_vcodec", "hclk_vcodec",
>> + "clk_cabac", "clk_core";
>> + assigned-clocks = <&cru ACLK_VDU_PRE>, <&cru SCLK_VDU_CA>,
>> + <&cru SCLK_VDU_CORE>;
>> + assigned-clock-parents = <&cru PLL_NPLL>, <&cru PLL_NPLL>,
>> + <&cru PLL_NPLL>;
>> + resets = <&cru SRST_H_VDU>, <&cru SRST_A_VDU>,
>> + <&cru SRST_VDU_CORE>, <&cru SRST_VDU_CA>,
>> + <&cru SRST_A_VDU_NOC>, <&cru SRST_H_VDU_NOC>;
>> + reset-names = "video_h", "video_a", "video_core", "video_cabac",
>> + "niu_a", "niu_h";
>> + power-domains = <&power RK3399_PD_VDU>;
>> + rockchip,srv = <&rkvdec_srv>;
>> + iommus = <&vdec_mmu>;
>> + status = "disabled";
>> + };
>> +
>> vdec_mmu: iommu@ff660480 {
>> compatible = "rockchip,iommu";
>> reg = <0x0 0xff660480 0x0 0x40>, <0x0 0xff6604c0 0x0 0x40>;
>
>


2019-01-06 16:07:13

by Ezequiel Garcia

[permalink] [raw]
Subject: Re: [PATCH 4/4] arm64: dts: rockchip: add video codec for rk3399

On Sun, 2019-01-06 at 23:05 +0800, Ayaka wrote:
> > On Jan 6, 2019, at 10:22 PM, Ezequiel Garcia <[email protected]> wrote:
> >
> > Hi Randy,
> >
> > Thanks a lot for this patches. They are really useful
> > to provide more insight into the VPU hardware.
> >
> > This change will make the vpu encoder and vpu decoder
> > completely independent, can they really work in parallel?
> As I said it depends on the platform, but with this patch, the user space would think they can work at the same time.


I think there is some confusion.

The devicetree is one thing: it is a hardware representation,
a way to describe the hardware, for the kernel/bootloader to
parse.

The userspace view will depend on the driver implementation.

The current devicetree and driver (without your patches),
model the VPU as a single piece of hardware, exposing a decoder
and an encoder.

The V4L driver will then create two video devices, i.e. /dev/videoX
and /dev/videoY. So userspace sees an independent view of the
devices.

However, they are internally connected, and thus we can
easily avoid two jobs running in parallel.

So, in other words, if the VPU can issue a decoder and encoder
job in parallel, then it's useful to model it as two independent
devices. Otherwise, it's better not to.

I hope this can clarify things a bit for you!

PS: Too bad I won't be at FOSDEM to discuss this personally.

Thanks,
Ezequiel

> BTW, not only the decoder, there is a post processor with the decoder, it can be used as part of the decoder pipeline with only a macro block delay
> or process the data from an external buffer.
> I forget to write a note on what this driver doesn’t present. The real one would have much complex scheduler system, but this one is just a queue.
> More task management feature are not there.
> Also the clock boosting is removing and the loading analysis, which are useful for encoder, especially on the rv1108.
> > Could you provide more details about what is
> > shared between these devices?
> No, if Rockchip doesn’t tell, my mouth is sealed.
> > Thanks a lot!
> >
> > > On Sun, 2019-01-06 at 02:31 +0800, Randy Li wrote:
> > > It offers an example how a full features video codec
> > > should be configured.
> > >
> > > The original clocks assignment don't look good, if the clocks
> > > lower than 300MHZ, most of decoing tasks would suffer from
> > > timeout problem, 500MHZ is also a little high for RK3399
> > > running in a stable state.
> > >
> > > Signed-off-by: Randy Li <[email protected]>
> > > ---
> > > .../boot/dts/rockchip/rk3399-sapphire.dtsi | 29 ++++++++
> > > arch/arm64/boot/dts/rockchip/rk3399.dtsi | 68 +++++++++++++++++--
> > > 2 files changed, 90 insertions(+), 7 deletions(-)
> > >
> > > diff --git a/arch/arm64/boot/dts/rockchip/rk3399-sapphire.dtsi b/arch/arm64/boot/dts/rockchip/rk3399-sapphire.dtsi
> > > index 946d3589575a..c3db878bae45 100644
> > > --- a/arch/arm64/boot/dts/rockchip/rk3399-sapphire.dtsi
> > > +++ b/arch/arm64/boot/dts/rockchip/rk3399-sapphire.dtsi
> > > @@ -632,6 +632,35 @@
> > > dr_mode = "host";
> > > };
> > >
> > > +&rkvdec {
> > > + status = "okay";
> > > +};
> > > +
> > > +&rkvdec_srv {
> > > + status = "okay";
> > > +};
> > > +
> > > +&vdec_mmu {
> > > + status = "okay";
> > > +};
> > > +
> > > +&vdpu {
> > > + status = "okay";
> > > +};
> > > +
> > > +&vepu {
> > > + status = "okay";
> > > +};
> > > +
> > > +&vpu_service {
> > > + status = "okay";
> > > +};
> > > +
> > > +&vpu_mmu {
> > > + status = "okay";
> > > +
> > > +};
> > > +
> > > &vopb {
> > > status = "okay";
> > > };
> > > diff --git a/arch/arm64/boot/dts/rockchip/rk3399.dtsi b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
> > > index b22b2e40422b..5fa3247e7bf0 100644
> > > --- a/arch/arm64/boot/dts/rockchip/rk3399.dtsi
> > > +++ b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
> > > @@ -1242,16 +1242,39 @@
> > > status = "disabled";
> > > };
> > >
> > > - vpu: video-codec@ff650000 {
> > > - compatible = "rockchip,rk3399-vpu";
> > > - reg = <0x0 0xff650000 0x0 0x800>;
> > > - interrupts = <GIC_SPI 114 IRQ_TYPE_LEVEL_HIGH 0>,
> > > - <GIC_SPI 113 IRQ_TYPE_LEVEL_HIGH 0>;
> > > - interrupt-names = "vepu", "vdpu";
> > > + vpu_service: vpu-srv {
> > > + compatible = "rockchip,mpp-service";
> > > + status = "disabled";
> > > + };
> > > +
> > > + vepu: vpu-encoder@ff650000 {
> > > + compatible = "rockchip,vpu-encoder-v2";
> > > + reg = <0x0 0xff650000 0x0 0x400>;
> > > + interrupts = <GIC_SPI 114 IRQ_TYPE_LEVEL_HIGH 0>;
> > > + interrupt-names = "irq_enc";
> > > clocks = <&cru ACLK_VCODEC>, <&cru HCLK_VCODEC>;
> > > - clock-names = "aclk", "hclk";
> > > + clock-names = "aclk_vcodec", "hclk_vcodec";
> > > + resets = <&cru SRST_H_VCODEC>, <&cru SRST_A_VCODEC>;
> > > + reset-names = "video_h", "video_a";
> > > iommus = <&vpu_mmu>;
> > > power-domains = <&power RK3399_PD_VCODEC>;
> > > + rockchip,srv = <&vpu_service>;
> > > + status = "disabled";
> > > + };
> > > +
> > > + vdpu: vpu-decoder@ff650400 {
> > > + compatible = "rockchip,vpu-decoder-v2";
> > > + reg = <0x0 0xff650400 0x0 0x400>;
> > > + interrupts = <GIC_SPI 113 IRQ_TYPE_LEVEL_HIGH 0>;
> > > + interrupt-names = "irq_dec";
> > > + iommus = <&vpu_mmu>;
> > > + clocks = <&cru ACLK_VCODEC>, <&cru HCLK_VCODEC>;
> > > + clock-names = "aclk_vcodec", "hclk_vcodec";
> > > + resets = <&cru SRST_H_VCODEC>, <&cru SRST_A_VCODEC>;
> > > + reset-names = "video_h", "video_a";
> > > + power-domains = <&power RK3399_PD_VCODEC>;
> > > + rockchip,srv = <&vpu_service>;
> > > + status = "disabled";
> > > };
> > >
> > > vpu_mmu: iommu@ff650800 {
> > > @@ -1261,11 +1284,42 @@
> > > interrupt-names = "vpu_mmu";
> > > clocks = <&cru ACLK_VCODEC>, <&cru HCLK_VCODEC>;
> > > clock-names = "aclk", "iface";
> > > + assigned-clocks = <&cru ACLK_VCODEC_PRE>;
> > > + assigned-clock-parents = <&cru PLL_GPLL>;
> > > #iommu-cells = <0>;
> > > power-domains = <&power RK3399_PD_VCODEC>;
> > > status = "disabled";
> > > };
> > >
> > > + rkvdec_srv: rkvdec-srv {
> > > + compatible = "rockchip,mpp-service";
> > > + status = "disabled";
> > > + };
> > > +
> > > + rkvdec: video-decoder@ff660000 {
> > > + compatible = "rockchip,video-decoder-v1";
> > > + reg = <0x0 0xff660000 0x0 0x400>;
> > > + interrupts = <GIC_SPI 116 IRQ_TYPE_LEVEL_HIGH 0>;
> > > + interrupt-names = "irq_dec";
> > > + clocks = <&cru ACLK_VDU>, <&cru HCLK_VDU>,
> > > + <&cru SCLK_VDU_CA>, <&cru SCLK_VDU_CORE>;
> > > + clock-names = "aclk_vcodec", "hclk_vcodec",
> > > + "clk_cabac", "clk_core";
> > > + assigned-clocks = <&cru ACLK_VDU_PRE>, <&cru SCLK_VDU_CA>,
> > > + <&cru SCLK_VDU_CORE>;
> > > + assigned-clock-parents = <&cru PLL_NPLL>, <&cru PLL_NPLL>,
> > > + <&cru PLL_NPLL>;
> > > + resets = <&cru SRST_H_VDU>, <&cru SRST_A_VDU>,
> > > + <&cru SRST_VDU_CORE>, <&cru SRST_VDU_CA>,
> > > + <&cru SRST_A_VDU_NOC>, <&cru SRST_H_VDU_NOC>;
> > > + reset-names = "video_h", "video_a", "video_core", "video_cabac",
> > > + "niu_a", "niu_h";
> > > + power-domains = <&power RK3399_PD_VDU>;
> > > + rockchip,srv = <&rkvdec_srv>;
> > > + iommus = <&vdec_mmu>;
> > > + status = "disabled";
> > > + };
> > > +
> > > vdec_mmu: iommu@ff660480 {
> > > compatible = "rockchip,iommu";
> > > reg = <0x0 0xff660480 0x0 0x40>, <0x0 0xff6604c0 0x0 0x40>;



2019-01-06 16:20:05

by Randy Li

[permalink] [raw]
Subject: Re: [PATCH 4/4] arm64: dts: rockchip: add video codec for rk3399



Sent from my iPad

> On Jan 7, 2019, at 12:04 AM, Ezequiel Garcia <[email protected]> wrote:
>
> On Sun, 2019-01-06 at 23:05 +0800, Ayaka wrote:
>>> On Jan 6, 2019, at 10:22 PM, Ezequiel Garcia <[email protected]> wrote:
>>>
>>> Hi Randy,
>>>
>>> Thanks a lot for this patches. They are really useful
>>> to provide more insight into the VPU hardware.
>>>
>>> This change will make the vpu encoder and vpu decoder
>>> completely independent, can they really work in parallel?
>> As I said it depends on the platform, but with this patch, the user space would think they can work at the same time.
>
>
> I think there is some confusion.
>
> The devicetree is one thing: it is a hardware representation,
> a way to describe the hardware, for the kernel/bootloader to
> parse.
>
> The userspace view will depend on the driver implementation.
>
> The current devicetree and driver (without your patches),
> model the VPU as a single piece of hardware, exposing a decoder
> and an encoder.
>
> The V4L driver will then create two video devices, i.e. /dev/videoX
> and /dev/videoY. So userspace sees an independent view of the
> devices.
>
I knew that, the problem is that the driver should not always create a decoder and encoder pair, they may not exist at some platforms, even some platforms doesn’t have a encoder. You may have a look on the rk3328 I post on the first email as example.
> However, they are internally connected, and thus we can
> easily avoid two jobs running in parallel.
>
That is what the mpp service did in my patches, handing the relationship between each devices. And it is not a easy work, maybe a 4k decoder would be blocked by another high frame rate encoding work or another decoder session. The vendor kernel have more worry about this, but not in this version.
> So, in other words, if the VPU can issue a decoder and encoder
> job in parallel, then it's useful to model it as two independent
> devices. Otherwise, it's better not to.
>
> I hope this can clarify things a bit for you!
>
I would review the request API for those codecs structures and some basic designs, people from LibreELEC told me a few noticed inform.
> PS: Too bad I won't be at FOSDEM to discuss this personally.
I am sure Paulk from bootlin would be there and some others guy related, I have not cared about this for a little long time, there are more huge problem I should point out now.
>
> Thanks,
> Ezequiel
>
>> BTW, not only the decoder, there is a post processor with the decoder, it can be used as part of the decoder pipeline with only a macro block delay
>> or process the data from an external buffer.
>> I forget to write a note on what this driver doesn’t present. The real one would have much complex scheduler system, but this one is just a queue.
>> More task management feature are not there.
>> Also the clock boosting is removing and the loading analysis, which are useful for encoder, especially on the rv1108.
>>> Could you provide more details about what is
>>> shared between these devices?
>> No, if Rockchip doesn’t tell, my mouth is sealed.
>>> Thanks a lot!
>>>
>>>> On Sun, 2019-01-06 at 02:31 +0800, Randy Li wrote:
>>>> It offers an example how a full features video codec
>>>> should be configured.
>>>>
>>>> The original clocks assignment don't look good, if the clocks
>>>> lower than 300MHZ, most of decoing tasks would suffer from
>>>> timeout problem, 500MHZ is also a little high for RK3399
>>>> running in a stable state.
>>>>
>>>> Signed-off-by: Randy Li <[email protected]>
>>>> ---
>>>> .../boot/dts/rockchip/rk3399-sapphire.dtsi | 29 ++++++++
>>>> arch/arm64/boot/dts/rockchip/rk3399.dtsi | 68 +++++++++++++++++--
>>>> 2 files changed, 90 insertions(+), 7 deletions(-)
>>>>
>>>> diff --git a/arch/arm64/boot/dts/rockchip/rk3399-sapphire.dtsi b/arch/arm64/boot/dts/rockchip/rk3399-sapphire.dtsi
>>>> index 946d3589575a..c3db878bae45 100644
>>>> --- a/arch/arm64/boot/dts/rockchip/rk3399-sapphire.dtsi
>>>> +++ b/arch/arm64/boot/dts/rockchip/rk3399-sapphire.dtsi
>>>> @@ -632,6 +632,35 @@
>>>> dr_mode = "host";
>>>> };
>>>>
>>>> +&rkvdec {
>>>> + status = "okay";
>>>> +};
>>>> +
>>>> +&rkvdec_srv {
>>>> + status = "okay";
>>>> +};
>>>> +
>>>> +&vdec_mmu {
>>>> + status = "okay";
>>>> +};
>>>> +
>>>> +&vdpu {
>>>> + status = "okay";
>>>> +};
>>>> +
>>>> +&vepu {
>>>> + status = "okay";
>>>> +};
>>>> +
>>>> +&vpu_service {
>>>> + status = "okay";
>>>> +};
>>>> +
>>>> +&vpu_mmu {
>>>> + status = "okay";
>>>> +
>>>> +};
>>>> +
>>>> &vopb {
>>>> status = "okay";
>>>> };
>>>> diff --git a/arch/arm64/boot/dts/rockchip/rk3399.dtsi b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
>>>> index b22b2e40422b..5fa3247e7bf0 100644
>>>> --- a/arch/arm64/boot/dts/rockchip/rk3399.dtsi
>>>> +++ b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
>>>> @@ -1242,16 +1242,39 @@
>>>> status = "disabled";
>>>> };
>>>>
>>>> - vpu: video-codec@ff650000 {
>>>> - compatible = "rockchip,rk3399-vpu";
>>>> - reg = <0x0 0xff650000 0x0 0x800>;
>>>> - interrupts = <GIC_SPI 114 IRQ_TYPE_LEVEL_HIGH 0>,
>>>> - <GIC_SPI 113 IRQ_TYPE_LEVEL_HIGH 0>;
>>>> - interrupt-names = "vepu", "vdpu";
>>>> + vpu_service: vpu-srv {
>>>> + compatible = "rockchip,mpp-service";
>>>> + status = "disabled";
>>>> + };
>>>> +
>>>> + vepu: vpu-encoder@ff650000 {
>>>> + compatible = "rockchip,vpu-encoder-v2";
>>>> + reg = <0x0 0xff650000 0x0 0x400>;
>>>> + interrupts = <GIC_SPI 114 IRQ_TYPE_LEVEL_HIGH 0>;
>>>> + interrupt-names = "irq_enc";
>>>> clocks = <&cru ACLK_VCODEC>, <&cru HCLK_VCODEC>;
>>>> - clock-names = "aclk", "hclk";
>>>> + clock-names = "aclk_vcodec", "hclk_vcodec";
>>>> + resets = <&cru SRST_H_VCODEC>, <&cru SRST_A_VCODEC>;
>>>> + reset-names = "video_h", "video_a";
>>>> iommus = <&vpu_mmu>;
>>>> power-domains = <&power RK3399_PD_VCODEC>;
>>>> + rockchip,srv = <&vpu_service>;
>>>> + status = "disabled";
>>>> + };
>>>> +
>>>> + vdpu: vpu-decoder@ff650400 {
>>>> + compatible = "rockchip,vpu-decoder-v2";
>>>> + reg = <0x0 0xff650400 0x0 0x400>;
>>>> + interrupts = <GIC_SPI 113 IRQ_TYPE_LEVEL_HIGH 0>;
>>>> + interrupt-names = "irq_dec";
>>>> + iommus = <&vpu_mmu>;
>>>> + clocks = <&cru ACLK_VCODEC>, <&cru HCLK_VCODEC>;
>>>> + clock-names = "aclk_vcodec", "hclk_vcodec";
>>>> + resets = <&cru SRST_H_VCODEC>, <&cru SRST_A_VCODEC>;
>>>> + reset-names = "video_h", "video_a";
>>>> + power-domains = <&power RK3399_PD_VCODEC>;
>>>> + rockchip,srv = <&vpu_service>;
>>>> + status = "disabled";
>>>> };
>>>>
>>>> vpu_mmu: iommu@ff650800 {
>>>> @@ -1261,11 +1284,42 @@
>>>> interrupt-names = "vpu_mmu";
>>>> clocks = <&cru ACLK_VCODEC>, <&cru HCLK_VCODEC>;
>>>> clock-names = "aclk", "iface";
>>>> + assigned-clocks = <&cru ACLK_VCODEC_PRE>;
>>>> + assigned-clock-parents = <&cru PLL_GPLL>;
>>>> #iommu-cells = <0>;
>>>> power-domains = <&power RK3399_PD_VCODEC>;
>>>> status = "disabled";
>>>> };
>>>>
>>>> + rkvdec_srv: rkvdec-srv {
>>>> + compatible = "rockchip,mpp-service";
>>>> + status = "disabled";
>>>> + };
>>>> +
>>>> + rkvdec: video-decoder@ff660000 {
>>>> + compatible = "rockchip,video-decoder-v1";
>>>> + reg = <0x0 0xff660000 0x0 0x400>;
>>>> + interrupts = <GIC_SPI 116 IRQ_TYPE_LEVEL_HIGH 0>;
>>>> + interrupt-names = "irq_dec";
>>>> + clocks = <&cru ACLK_VDU>, <&cru HCLK_VDU>,
>>>> + <&cru SCLK_VDU_CA>, <&cru SCLK_VDU_CORE>;
>>>> + clock-names = "aclk_vcodec", "hclk_vcodec",
>>>> + "clk_cabac", "clk_core";
>>>> + assigned-clocks = <&cru ACLK_VDU_PRE>, <&cru SCLK_VDU_CA>,
>>>> + <&cru SCLK_VDU_CORE>;
>>>> + assigned-clock-parents = <&cru PLL_NPLL>, <&cru PLL_NPLL>,
>>>> + <&cru PLL_NPLL>;
>>>> + resets = <&cru SRST_H_VDU>, <&cru SRST_A_VDU>,
>>>> + <&cru SRST_VDU_CORE>, <&cru SRST_VDU_CA>,
>>>> + <&cru SRST_A_VDU_NOC>, <&cru SRST_H_VDU_NOC>;
>>>> + reset-names = "video_h", "video_a", "video_core", "video_cabac",
>>>> + "niu_a", "niu_h";
>>>> + power-domains = <&power RK3399_PD_VDU>;
>>>> + rockchip,srv = <&rkvdec_srv>;
>>>> + iommus = <&vdec_mmu>;
>>>> + status = "disabled";
>>>> + };
>>>> +
>>>> vdec_mmu: iommu@ff660480 {
>>>> compatible = "rockchip,iommu";
>>>> reg = <0x0 0xff660480 0x0 0x40>, <0x0 0xff6604c0 0x0 0x40>;
>
>


2019-01-06 17:23:10

by Ezequiel Garcia

[permalink] [raw]
Subject: Re: [PATCH 4/4] arm64: dts: rockchip: add video codec for rk3399

On Sun, 6 Jan 2019 at 13:16, Ayaka <[email protected]> wrote:
>
>
>
> Sent from my iPad
>
> > On Jan 7, 2019, at 12:04 AM, Ezequiel Garcia <[email protected]> wrote:
> >
> > On Sun, 2019-01-06 at 23:05 +0800, Ayaka wrote:
> >>> On Jan 6, 2019, at 10:22 PM, Ezequiel Garcia <[email protected]> wrote:
> >>>
> >>> Hi Randy,
> >>>
> >>> Thanks a lot for this patches. They are really useful
> >>> to provide more insight into the VPU hardware.
> >>>
> >>> This change will make the vpu encoder and vpu decoder
> >>> completely independent, can they really work in parallel?
> >> As I said it depends on the platform, but with this patch, the user space would think they can work at the same time.
> >
> >
> > I think there is some confusion.
> >
> > The devicetree is one thing: it is a hardware representation,
> > a way to describe the hardware, for the kernel/bootloader to
> > parse.
> >
> > The userspace view will depend on the driver implementation.
> >
> > The current devicetree and driver (without your patches),
> > model the VPU as a single piece of hardware, exposing a decoder
> > and an encoder.
> >
> > The V4L driver will then create two video devices, i.e. /dev/videoX
> > and /dev/videoY. So userspace sees an independent view of the
> > devices.
> >
> I knew that, the problem is that the driver should not always create a decoder and encoder pair, they may not exist at some platforms, even some platforms doesn’t have a encoder. You may have a look on the rk3328 I post on the first email as example.

That is correct. But that still doesn't tackle my question: is the
hardware able to run a decoding and an encoding job in parallel?

If not, then it's wrong to describe them as independent entities.

> > However, they are internally connected, and thus we can
> > easily avoid two jobs running in parallel.
> >
> That is what the mpp service did in my patches, handing the relationship between each devices. And it is not a easy work, maybe a 4k decoder would be blocked by another high frame rate encoding work or another decoder session. The vendor kernel have more worry about this, but not in this version.

Right. That is one way to design it. Another way is having a single
devicetree node for the VPU encoder/decoder "complex".

Thanks for the input!
--
Ezequiel García, VanguardiaSur
http://www.vanguardiasur.com.ar

2019-01-06 17:31:30

by Randy Li

[permalink] [raw]
Subject: Re: [PATCH 4/4] arm64: dts: rockchip: add video codec for rk3399

Hello Ezequiel

Sent from my iPad

> On Jan 7, 2019, at 1:21 AM, Ezequiel Garcia <[email protected]> wrote:
>
>> On Sun, 6 Jan 2019 at 13:16, Ayaka <[email protected]> wrote:
>>
>>
>>
>> Sent from my iPad
>>
>>> On Jan 7, 2019, at 12:04 AM, Ezequiel Garcia <[email protected]> wrote:
>>>
>>> On Sun, 2019-01-06 at 23:05 +0800, Ayaka wrote:
>>>>> On Jan 6, 2019, at 10:22 PM, Ezequiel Garcia <[email protected]> wrote:
>>>>>
>>>>> Hi Randy,
>>>>>
>>>>> Thanks a lot for this patches. They are really useful
>>>>> to provide more insight into the VPU hardware.
>>>>>
>>>>> This change will make the vpu encoder and vpu decoder
>>>>> completely independent, can they really work in parallel?
>>>> As I said it depends on the platform, but with this patch, the user space would think they can work at the same time.
>>>
>>>
>>> I think there is some confusion.
>>>
>>> The devicetree is one thing: it is a hardware representation,
>>> a way to describe the hardware, for the kernel/bootloader to
>>> parse.
>>>
>>> The userspace view will depend on the driver implementation.
>>>
>>> The current devicetree and driver (without your patches),
>>> model the VPU as a single piece of hardware, exposing a decoder
>>> and an encoder.
>>>
>>> The V4L driver will then create two video devices, i.e. /dev/videoX
>>> and /dev/videoY. So userspace sees an independent view of the
>>> devices.
>>>
>> I knew that, the problem is that the driver should not always create a decoder and encoder pair, they may not exist at some platforms, even some platforms doesn’t have a encoder. You may have a look on the rk3328 I post on the first email as example.
>
> That is correct. But that still doesn't tackle my question: is the
> hardware able to run a decoding and an encoding job in parallel?
>
For rk3328, yes, you see I didn’t draw them in the same box.
> If not, then it's wrong to describe them as independent entities.
>
>>> However, they are internally connected, and thus we can
>>> easily avoid two jobs running in parallel.
>>>
>> That is what the mpp service did in my patches, handing the relationship between each devices. And it is not a easy work, maybe a 4k decoder would be blocked by another high frame rate encoding work or another decoder session. The vendor kernel have more worry about this, but not in this version.
>
> Right. That is one way to design it. Another way is having a single
> devicetree node for the VPU encoder/decoder "complex".
No, you can’t assume which one is in the combo group, it can be various. you see, in the rk3328, the vdpu is paired with an avs+ decoder. That is why I use a virtual device standing for scheduler.
>
> Thanks for the input!
> --
> Ezequiel García, VanguardiaSur
> http://www.vanguardiasur.com.ar


2019-01-08 06:35:28

by Tomasz Figa

[permalink] [raw]
Subject: Re: [PATCH 4/4] arm64: dts: rockchip: add video codec for rk3399

On Mon, Jan 7, 2019 at 2:30 AM Ayaka <[email protected]> wrote:
>
> Hello Ezequiel
>
> Sent from my iPad
>
> > On Jan 7, 2019, at 1:21 AM, Ezequiel Garcia <[email protected]> wrote:
> >
> >> On Sun, 6 Jan 2019 at 13:16, Ayaka <[email protected]> wrote:
> >>
> >>
> >>
> >> Sent from my iPad
> >>
> >>> On Jan 7, 2019, at 12:04 AM, Ezequiel Garcia <[email protected]> wrote:
> >>>
> >>> On Sun, 2019-01-06 at 23:05 +0800, Ayaka wrote:
> >>>>> On Jan 6, 2019, at 10:22 PM, Ezequiel Garcia <[email protected]> wrote:
> >>>>>
> >>>>> Hi Randy,
> >>>>>
> >>>>> Thanks a lot for this patches. They are really useful
> >>>>> to provide more insight into the VPU hardware.
> >>>>>
> >>>>> This change will make the vpu encoder and vpu decoder
> >>>>> completely independent, can they really work in parallel?
> >>>> As I said it depends on the platform, but with this patch, the user space would think they can work at the same time.
> >>>
> >>>
> >>> I think there is some confusion.
> >>>
> >>> The devicetree is one thing: it is a hardware representation,
> >>> a way to describe the hardware, for the kernel/bootloader to
> >>> parse.
> >>>
> >>> The userspace view will depend on the driver implementation.
> >>>
> >>> The current devicetree and driver (without your patches),
> >>> model the VPU as a single piece of hardware, exposing a decoder
> >>> and an encoder.
> >>>
> >>> The V4L driver will then create two video devices, i.e. /dev/videoX
> >>> and /dev/videoY. So userspace sees an independent view of the
> >>> devices.
> >>>
> >> I knew that, the problem is that the driver should not always create a decoder and encoder pair, they may not exist at some platforms, even some platforms doesn’t have a encoder. You may have a look on the rk3328 I post on the first email as example.
> >
> > That is correct. But that still doesn't tackle my question: is the
> > hardware able to run a decoding and an encoding job in parallel?
> >
> For rk3328, yes, you see I didn’t draw them in the same box.
> > If not, then it's wrong to describe them as independent entities.
> >
> >>> However, they are internally connected, and thus we can
> >>> easily avoid two jobs running in parallel.
> >>>
> >> That is what the mpp service did in my patches, handing the relationship between each devices. And it is not a easy work, maybe a 4k decoder would be blocked by another high frame rate encoding work or another decoder session. The vendor kernel have more worry about this, but not in this version.
> >
> > Right. That is one way to design it. Another way is having a single
> > devicetree node for the VPU encoder/decoder "complex".
> No, you can’t assume which one is in the combo group, it can be various. you see, in the rk3328, the vdpu is paired with an avs+ decoder. That is why I use a virtual device standing for scheduler.

First of all, thanks for all the input. Having more understanding of
the hardware and shortcomings of the current V4L2 APIs is really
important to let us further evolve the API and make sure that it works
for further use cases.

As for the Device Tree itself, it doesn't always describe the hardware
in 100%. Most of the time it's just the necessary information to
choose and instantiate the right drivers and bind to the right
hardware resources. The information on which hardware instances on the
SoC can work independently can of course be described in DT (e.g. by
sub-nodes of a video-codec complex OR a set of phandles, e.g.
rockchip,shared-instances), but it's also perfectly fine to defer this
kind of knowledge to the drivers themselves.

Best regards,
Tomasz

2019-01-08 07:41:32

by Randy Li

[permalink] [raw]
Subject: Re: [PATCH 4/4] arm64: dts: rockchip: add video codec for rk3399



Sent from my iPad

> On Jan 8, 2019, at 2:33 PM, Tomasz Figa <[email protected]> wrote:
>
>> On Mon, Jan 7, 2019 at 2:30 AM Ayaka <[email protected]> wrote:
>>
>> Hello Ezequiel
>>
>> Sent from my iPad
>>
>>>> On Jan 7, 2019, at 1:21 AM, Ezequiel Garcia <[email protected]> wrote:
>>>>
>>>> On Sun, 6 Jan 2019 at 13:16, Ayaka <[email protected]> wrote:
>>>>
>>>>
>>>>
>>>> Sent from my iPad
>>>>
>>>>> On Jan 7, 2019, at 12:04 AM, Ezequiel Garcia <[email protected]> wrote:
>>>>>
>>>>> On Sun, 2019-01-06 at 23:05 +0800, Ayaka wrote:
>>>>>>> On Jan 6, 2019, at 10:22 PM, Ezequiel Garcia <[email protected]> wrote:
>>>>>>>
>>>>>>> Hi Randy,
>>>>>>>
>>>>>>> Thanks a lot for this patches. They are really useful
>>>>>>> to provide more insight into the VPU hardware.
>>>>>>>
>>>>>>> This change will make the vpu encoder and vpu decoder
>>>>>>> completely independent, can they really work in parallel?
>>>>>> As I said it depends on the platform, but with this patch, the user space would think they can work at the same time.
>>>>>
>>>>>
>>>>> I think there is some confusion.
>>>>>
>>>>> The devicetree is one thing: it is a hardware representation,
>>>>> a way to describe the hardware, for the kernel/bootloader to
>>>>> parse.
>>>>>
>>>>> The userspace view will depend on the driver implementation.
>>>>>
>>>>> The current devicetree and driver (without your patches),
>>>>> model the VPU as a single piece of hardware, exposing a decoder
>>>>> and an encoder.
>>>>>
>>>>> The V4L driver will then create two video devices, i.e. /dev/videoX
>>>>> and /dev/videoY. So userspace sees an independent view of the
>>>>> devices.
>>>>>
>>>> I knew that, the problem is that the driver should not always create a decoder and encoder pair, they may not exist at some platforms, even some platforms doesn’t have a encoder. You may have a look on the rk3328 I post on the first email as example.
>>>
>>> That is correct. But that still doesn't tackle my question: is the
>>> hardware able to run a decoding and an encoding job in parallel?
>>>
>> For rk3328, yes, you see I didn’t draw them in the same box.
>>> If not, then it's wrong to describe them as independent entities.
>>>
>>>>> However, they are internally connected, and thus we can
>>>>> easily avoid two jobs running in parallel.
>>>>>
>>>> That is what the mpp service did in my patches, handing the relationship between each devices. And it is not a easy work, maybe a 4k decoder would be blocked by another high frame rate encoding work or another decoder session. The vendor kernel have more worry about this, but not in this version.
>>>
>>> Right. That is one way to design it. Another way is having a single
>>> devicetree node for the VPU encoder/decoder "complex".
>> No, you can’t assume which one is in the combo group, it can be various. you see, in the rk3328, the vdpu is paired with an avs+ decoder. That is why I use a virtual device standing for scheduler.
>
> First of all, thanks for all the input. Having more understanding of
> the hardware and shortcomings of the current V4L2 APIs is really
> important to let us further evolve the API and make sure that it works
> for further use cases.
I replied the problems of the v4l2 request API in the other threads. I am waiting the feedback from those threads.
>
> As for the Device Tree itself, it doesn't always describe the hardware
> in 100%.
Also please note the merged device tree for the video codec won’t fix for most of the rockchip platform.
> Most of the time it's just the necessary information to
> choose and instantiate the right drivers and bind to the right
> hardware resources. The information on which hardware instances on the
> SoC can work independently can of course be described in DT (e.g. by
> sub-nodes of a video-codec complex OR a set of phandles, e.g.
> rockchip,shared-instances), but it's also perfectly fine to defer this
> kind of knowledge to the drivers themselves.
I wish there is a common mechanism for those device would share some resources. Although there is a multiple functions framework, but that is not I want. They are multiple functions but they are used at the same time not separately.
>
> Best regards,
> Tomasz