media: mtk-vpu: VPU should be in idle state before system is suspended
authorIrui Wang <irui.wang@mediatek.com>
Thu, 29 Oct 2020 01:17:20 +0000 (02:17 +0100)
committerMauro Carvalho Chehab <mchehab+huawei@kernel.org>
Thu, 3 Dec 2020 06:42:09 +0000 (07:42 +0100)
VPU should be in idle state before system is suspended
or it will work abnormally like VPU program counter not
in a correct address or VPU reset

Signed-off-by: Irui Wang <irui.wang@mediatek.com>
Reviewed-by: Alexandre Courbot <acourbot@chromium.org>
Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
drivers/media/platform/mtk-vpu/mtk_vpu.c

index 36cb9b6..86ab808 100644 (file)
@@ -27,6 +27,7 @@
 
 #define INIT_TIMEOUT_MS                2000U
 #define IPI_TIMEOUT_MS         2000U
+#define VPU_IDLE_TIMEOUT_MS    1000U
 #define VPU_FW_VER_LEN         16
 
 /* maximum program/data TCM (Tightly-Coupled Memory) size */
 #define VPU_DMEM_EXT0_ADDR     0x0014
 #define VPU_DMEM_EXT1_ADDR     0x0018
 #define HOST_TO_VPU            0x0024
+#define VPU_IDLE_REG           0x002C
+#define VPU_INT_STATUS         0x0034
 #define VPU_PC_REG             0x0060
 #define VPU_WDT_REG            0x0084
 
 /* vpu inter-processor communication interrupt */
 #define VPU_IPC_INT            BIT(8)
+/* vpu idle state */
+#define VPU_IDLE_STATE         BIT(23)
 
 /**
  * enum vpu_fw_type - VPU firmware type
@@ -945,11 +950,74 @@ static int mtk_vpu_remove(struct platform_device *pdev)
        return 0;
 }
 
+static int mtk_vpu_suspend(struct device *dev)
+{
+       struct mtk_vpu *vpu = dev_get_drvdata(dev);
+       unsigned long timeout;
+       int ret;
+
+       ret = vpu_clock_enable(vpu);
+       if (ret) {
+               dev_err(dev, "failed to enable vpu clock\n");
+               return ret;
+       }
+
+       mutex_lock(&vpu->vpu_mutex);
+       /* disable vpu timer interrupt */
+       vpu_cfg_writel(vpu, vpu_cfg_readl(vpu, VPU_INT_STATUS) | VPU_IDLE_STATE,
+                      VPU_INT_STATUS);
+       /* check if vpu is idle for system suspend */
+       timeout = jiffies + msecs_to_jiffies(VPU_IDLE_TIMEOUT_MS);
+       do {
+               if (time_after(jiffies, timeout)) {
+                       dev_err(dev, "vpu idle timeout\n");
+                       mutex_unlock(&vpu->vpu_mutex);
+                       vpu_clock_disable(vpu);
+                       return -EIO;
+               }
+       } while (!vpu_cfg_readl(vpu, VPU_IDLE_REG));
+
+       mutex_unlock(&vpu->vpu_mutex);
+       vpu_clock_disable(vpu);
+       clk_unprepare(vpu->clk);
+
+       return 0;
+}
+
+static int mtk_vpu_resume(struct device *dev)
+{
+       struct mtk_vpu *vpu = dev_get_drvdata(dev);
+       int ret;
+
+       clk_prepare(vpu->clk);
+       ret = vpu_clock_enable(vpu);
+       if (ret) {
+               dev_err(dev, "failed to enable vpu clock\n");
+               return ret;
+       }
+
+       mutex_lock(&vpu->vpu_mutex);
+       /* enable vpu timer interrupt */
+       vpu_cfg_writel(vpu,
+                      vpu_cfg_readl(vpu, VPU_INT_STATUS) & ~(VPU_IDLE_STATE),
+                      VPU_INT_STATUS);
+       mutex_unlock(&vpu->vpu_mutex);
+       vpu_clock_disable(vpu);
+
+       return 0;
+}
+
+static const struct dev_pm_ops mtk_vpu_pm = {
+       .suspend = mtk_vpu_suspend,
+       .resume = mtk_vpu_resume,
+};
+
 static struct platform_driver mtk_vpu_driver = {
        .probe  = mtk_vpu_probe,
        .remove = mtk_vpu_remove,
        .driver = {
                .name   = "mtk_vpu",
+               .pm = &mtk_vpu_pm,
                .of_match_table = mtk_vpu_match,
        },
 };