From e820bdd484faa4af32620e4c8a07c92de3d1d096 Mon Sep 17 00:00:00 2001
From: Alex Bee <knaerzche@gmail.com>
Date: Wed, 20 May 2020 17:04:47 +0200
Subject: [PATCH 31/59] WIP/1001: media: rkvdec: implement reset controls

---
 .../bindings/media/rockchip,vdec.yaml         | 19 +++++++
 drivers/staging/media/rkvdec/rkvdec-regs.h    |  5 ++
 drivers/staging/media/rkvdec/rkvdec.c         | 53 +++++++++++++++++++
 drivers/staging/media/rkvdec/rkvdec.h         | 11 +++-
 4 files changed, 87 insertions(+), 1 deletion(-)

diff --git a/Documentation/devicetree/bindings/media/rockchip,vdec.yaml b/Documentation/devicetree/bindings/media/rockchip,vdec.yaml
index 08b02ec16755..828d085b0ad3 100644
--- a/Documentation/devicetree/bindings/media/rockchip,vdec.yaml
+++ b/Documentation/devicetree/bindings/media/rockchip,vdec.yaml
@@ -53,6 +53,18 @@ properties:
   iommus:
     maxItems: 1
 
+  resets:
+    maxItems: 6
+
+  reset-names:
+    items:
+      - const: video_h
+      - const: video_a
+      - const: video_core
+      - const: video_cabac
+      - const: niu_a
+      - const: niu_h
+
 required:
   - compatible
   - reg
@@ -60,6 +72,8 @@ required:
   - clocks
   - clock-names
   - power-domains
+  - resets
+  - reset-names
 
 additionalProperties: false
 
@@ -78,6 +92,11 @@ examples:
         clock-names = "axi", "ahb", "cabac", "core";
         power-domains = <&power RK3399_PD_VDU>;
         iommus = <&vdec_mmu>;
+        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";
     };
 
 ...
diff --git a/drivers/staging/media/rkvdec/rkvdec-regs.h b/drivers/staging/media/rkvdec/rkvdec-regs.h
index 15b9bee92016..3acc914888f6 100644
--- a/drivers/staging/media/rkvdec/rkvdec-regs.h
+++ b/drivers/staging/media/rkvdec/rkvdec-regs.h
@@ -28,6 +28,11 @@
 #define RKVDEC_SOFTRST_EN_P				BIT(20)
 #define RKVDEC_FORCE_SOFTRESET_VALID			BIT(21)
 #define RKVDEC_SOFTRESET_RDY				BIT(22)
+#define RKVDEC_ERR_MASK					(RKVDEC_BUS_STA \
+							 | RKVDEC_ERR_STA \
+							 | RKVDEC_TIMEOUT_STA \
+							 | RKVDEC_BUF_EMPTY_STA \
+							 | RKVDEC_COLMV_REF_ERR_STA )
 
 #define RKVDEC_REG_SYSCTRL				0x008
 #define RKVDEC_IN_ENDIAN				BIT(0)
diff --git a/drivers/staging/media/rkvdec/rkvdec.c b/drivers/staging/media/rkvdec/rkvdec.c
index 7747e8396626..c9c59d090bb3 100644
--- a/drivers/staging/media/rkvdec/rkvdec.c
+++ b/drivers/staging/media/rkvdec/rkvdec.c
@@ -10,12 +10,15 @@
  */
 
 #include <linux/clk.h>
+#include <linux/delay.h>
 #include <linux/interrupt.h>
 #include <linux/module.h>
 #include <linux/of.h>
 #include <linux/platform_device.h>
 #include <linux/pm.h>
 #include <linux/pm_runtime.h>
+#include <linux/reset.h>
+#include <linux/rockchip_pmu.h>
 #include <linux/slab.h>
 #include <linux/videodev2.h>
 #include <linux/workqueue.h>
@@ -766,6 +769,11 @@ static void rkvdec_job_finish(struct rkvdec_ctx *ctx,
 
 	pm_runtime_mark_last_busy(rkvdec->dev);
 	pm_runtime_put_autosuspend(rkvdec->dev);
+
+	if (result == VB2_BUF_STATE_ERROR &&
+	    rkvdec->reset_mask == RESET_NONE)
+		rkvdec->reset_mask |= RESET_SOFT;
+
 	rkvdec_job_finish_no_pm(ctx, result);
 }
 
@@ -803,6 +811,33 @@ static void rkvdec_device_run(void *priv)
 
 	if (WARN_ON(!desc))
 		return;
+	if (rkvdec->reset_mask != RESET_NONE) {
+
+		if (rkvdec->reset_mask & RESET_SOFT) {
+			writel(RKVDEC_SOFTRST_EN_P,
+			       rkvdec->regs + RKVDEC_REG_INTERRUPT);
+			udelay(RKVDEC_RESET_DELAY);
+			if (readl(rkvdec->regs + RKVDEC_REG_INTERRUPT)
+			    & RKVDEC_SOFTRESET_RDY)
+				dev_info_ratelimited(rkvdec->dev,
+						      "softreset failed\n");
+		}
+
+		if (rkvdec->reset_mask & RESET_HARD) {
+			rockchip_pmu_idle_request(rkvdec->dev, true);
+			ret = reset_control_assert(rkvdec->rstc);
+			if (!ret) {
+				udelay(RKVDEC_RESET_DELAY);
+				ret = reset_control_deassert(rkvdec->rstc);
+			}
+			rockchip_pmu_idle_request(rkvdec->dev, false);
+			if (ret)
+				dev_notice_ratelimited(rkvdec->dev,
+							"hardreset failed\n");
+		}
+		rkvdec->reset_mask = RESET_NONE;
+		pm_runtime_suspend(rkvdec->dev);
+	}
 
 	ret = pm_runtime_resume_and_get(rkvdec->dev);
 	if (ret < 0) {
@@ -1069,6 +1104,11 @@ static irqreturn_t rkvdec_irq_handler(int irq, void *priv)
 	if (cancel_delayed_work(&rkvdec->watchdog_work)) {
 		struct rkvdec_ctx *ctx;
 
+		if (state == VB2_BUF_STATE_ERROR) {
+			rkvdec->reset_mask |= (status & RKVDEC_ERR_MASK) ?
+						RESET_HARD : RESET_SOFT;
+		}
+
 		ctx = v4l2_m2m_get_curr_priv(rkvdec->m2m_dev);
 		rkvdec_job_finish(ctx, state);
 	}
@@ -1086,6 +1126,7 @@ static void rkvdec_watchdog_func(struct work_struct *work)
 	ctx = v4l2_m2m_get_curr_priv(rkvdec->m2m_dev);
 	if (ctx) {
 		dev_err(rkvdec->dev, "Frame processing timed out!\n");
+		rkvdec->reset_mask |= RESET_HARD;
 		writel(RKVDEC_IRQ_DIS, rkvdec->regs + RKVDEC_REG_INTERRUPT);
 		writel(0, rkvdec->regs + RKVDEC_REG_SYSCTRL);
 		rkvdec_job_finish(ctx, VB2_BUF_STATE_ERROR);
@@ -1154,6 +1195,18 @@ static int rkvdec_probe(struct platform_device *pdev)
 		return ret;
 	}
 
+
+	rkvdec->rstc = devm_reset_control_array_get(&pdev->dev, RESET_CONTROL_OPTIONAL_EXCLUSIVE);
+	if (IS_ERR(rkvdec->rstc)) {
+		dev_err(&pdev->dev,
+			"get resets failed %ld\n", PTR_ERR(rkvdec->rstc));
+		return PTR_ERR(rkvdec->rstc);
+	} else {
+		dev_dbg(&pdev->dev,
+			 "requested %d resets\n",
+			 reset_control_get_count(&pdev->dev));
+	}
+
 	pm_runtime_set_autosuspend_delay(&pdev->dev, 100);
 	pm_runtime_use_autosuspend(&pdev->dev);
 	pm_runtime_enable(&pdev->dev);
diff --git a/drivers/staging/media/rkvdec/rkvdec.h b/drivers/staging/media/rkvdec/rkvdec.h
index 9a9f4fced7a1..101dfb4ec389 100644
--- a/drivers/staging/media/rkvdec/rkvdec.h
+++ b/drivers/staging/media/rkvdec/rkvdec.h
@@ -11,10 +11,11 @@
 #ifndef RKVDEC_H_
 #define RKVDEC_H_
 
+#include <linux/clk.h>
 #include <linux/platform_device.h>
+#include <linux/reset.h>
 #include <linux/videodev2.h>
 #include <linux/wait.h>
-#include <linux/clk.h>
 
 #include <media/v4l2-ctrls.h>
 #include <media/v4l2-device.h>
@@ -22,6 +23,12 @@
 #include <media/videobuf2-core.h>
 #include <media/videobuf2-dma-contig.h>
 
+#define RESET_NONE		0
+#define RESET_SOFT		BIT(0)
+#define RESET_HARD		BIT(1)
+
+#define RKVDEC_RESET_DELAY	5
+
 struct rkvdec_ctx;
 
 struct rkvdec_ctrl_desc {
@@ -110,6 +117,8 @@ struct rkvdec_dev {
 	void __iomem *regs;
 	struct mutex vdev_lock; /* serializes ioctls */
 	struct delayed_work watchdog_work;
+	struct reset_control *rstc;
+	u8 reset_mask;
 };
 
 struct rkvdec_ctx {
-- 
2.34.1

