kernel/0106-feat-DW200-Dw200-Support-power-management.patch
2024-12-19 16:34:44 -05:00

387 lines
12 KiB
Diff

From 242650c48b5b7b048d071cd866a5a907e89f22f0 Mon Sep 17 00:00:00 2001
From: lilijun <lilijun@eswincomputing.com>
Date: Wed, 17 Jul 2024 10:14:04 +0800
Subject: [PATCH 106/219] feat(DW200): Dw200 Support power management
Changelogs:
1. Add generic & runtime power management for dw200.
Signed-off-by: lilijun <lilijun@eswincomputing.com>
---
.../staging/media/eswin/dewarp/dw200_subdev.h | 1 +
.../media/eswin/dewarp/vvcam_dwe_driver.c | 179 ++++++++++++++----
2 files changed, 148 insertions(+), 32 deletions(-)
diff --git a/drivers/staging/media/eswin/dewarp/dw200_subdev.h b/drivers/staging/media/eswin/dewarp/dw200_subdev.h
index f95b7561465f..2d599f840cc5 100644
--- a/drivers/staging/media/eswin/dewarp/dw200_subdev.h
+++ b/drivers/staging/media/eswin/dewarp/dw200_subdev.h
@@ -310,6 +310,7 @@ enum {
#define VSE_DMABUF_DONE (1 << 1)
typedef struct _dw_clk_rst {
+ struct device *dev; // used for tbu enanle/disable
struct clk *aclk;
struct clk *cfg_clk;
struct clk *dw_aclk;
diff --git a/drivers/staging/media/eswin/dewarp/vvcam_dwe_driver.c b/drivers/staging/media/eswin/dewarp/vvcam_dwe_driver.c
index 2810f0a57d80..7ee5a88a803c 100644
--- a/drivers/staging/media/eswin/dewarp/vvcam_dwe_driver.c
+++ b/drivers/staging/media/eswin/dewarp/vvcam_dwe_driver.c
@@ -72,6 +72,8 @@
#include <linux/bitfield.h>
#include <linux/regmap.h>
#include <linux/eswin-win2030-sid-cfg.h>
+#include <linux/pm_runtime.h>
+#include <linux/clk.h>
#include "dw200_fe.h"
#include "dw200_ioctl.h"
@@ -108,6 +110,10 @@ struct es_dewarp_driver_dev {
unsigned int irq_num_vse;
bool irq_trigger;
wait_queue_head_t irq_wait;
+
+ wait_queue_head_t trigger_wq;
+ atomic_t trigger_atom;
+ int suspended;
};
struct es_dw200_private {
@@ -402,18 +408,17 @@ irqreturn_t dwe_isr(int irq, void *dev_id)
(struct dw200_subdev *)pdriver_dev->private;
u32 dwe_mis = 0;
unsigned long flags;
- pr_debug("%s enter\n", __func__);
spin_lock_irqsave(&dwe_irq_lock, flags);
dwe_read_irq((struct dw200_subdev *)pdw200, &dwe_mis);
- DEBUG_PRINT(" %s dwe mis 0x%08x\n", __func__, dwe_mis);
dwe_mis = dwe_mis & 0x1;
if (0 != dwe_mis) {
dwe_clear_irq((struct dw200_subdev *)pdw200, dwe_mis << 24);
atomic_set(&dwe_irq_trigger_mis, dwe_mis);
wake_up_interruptible_all(&dwe_irq_wait_q);
+ atomic_dec(&pdriver_dev->trigger_atom);
+ wake_up_interruptible(&pdriver_dev->trigger_wq);
spin_unlock_irqrestore(&dwe_irq_lock, flags);
- DEBUG_PRINT("%s exit\n", __func__);
return IRQ_HANDLED;
}
spin_unlock_irqrestore(&dwe_irq_lock, flags);
@@ -437,6 +442,8 @@ irqreturn_t vse_isr(int irq, void *dev_id)
vse_clear_irq((struct dw200_subdev *)pdw200, vse_mis);
atomic_set(&vse_irq_trigger_mis, vse_mis);
wake_up_interruptible_all(&vse_irq_wait_q);
+ atomic_dec(&pdriver_dev->trigger_atom);
+ wake_up_interruptible(&pdriver_dev->trigger_wq);
spin_unlock_irqrestore(&vse_irq_lock, flags);
DEBUG_PRINT("%s vse frame ready, vse mis 0x%08x\n", __func__,
vse_mis);
@@ -452,7 +459,6 @@ static long waitDweDone(struct es_dw200_private *pes_dw200_priv, long timeout)
u32 reg = 0;
u32 irq_trigger = 0;
struct dw200_subdev *pdwe_dev = &pes_dw200_priv->dw200;
- pr_debug("wait dwe done start\n");
ret = wait_event_interruptible_timeout(dwe_irq_wait_q, CheckIrq(1),
timeout);
// ret = wait_event_interruptible(dwe_irq_wait_q, atomic_read(&dwe_irq_trigger_mis));
@@ -492,7 +498,6 @@ static long waitVseDone(struct es_dw200_private *pes_dw200_priv, long timeout)
int ret = -1;
struct dw200_subdev *pdwe_dev = &pes_dw200_priv->dw200;
- pr_debug("wait vse done start\n");
ret = wait_event_interruptible_timeout(vse_irq_wait_q, CheckIrq(0),
timeout);
// ret = wait_event_interruptible(vse_irq_wait_q, CheckIrq(0));
@@ -524,6 +529,7 @@ long es_dw200_ioctl(struct es_dw200_private *pes_dw200_priv, unsigned int cmd,
{
int ret = -1;
struct dw200_subdev *pdwe_dev = &pes_dw200_priv->dw200;
+ struct es_dewarp_driver_dev *pdriver_dev = pes_dw200_priv->pdriver_dev;
u64 addr;
switch (cmd) {
@@ -590,18 +596,21 @@ long es_dw200_ioctl(struct es_dw200_private *pes_dw200_priv, unsigned int cmd,
// Read private data,and configure vse's regs,
// and start hw
pr_debug("trigger vse\n");
+ atomic_inc(&pdriver_dev->trigger_atom);
triggerVse(pes_dw200_priv);
return 0;
case DWIOC_ES_TRIGGER_DWE:
// Read private data,and configure dwe's regs,
// and start hw
pr_debug("trigger dwe\n");
+ atomic_inc(&pdriver_dev->trigger_atom);
triggerDwe(pes_dw200_priv);
return 0;
case DWIOC_ES_TRIGGER_DWE_VSE:
// Read private data,and configure regs,
// and start hw
pr_debug("trigger dwe online vse\n");
+ atomic_inc(&pdriver_dev->trigger_atom);
triggerDweVse(pes_dw200_priv);
return 0;
@@ -879,6 +888,8 @@ static int dewarp_open(struct inode *inode, struct file *file)
pdw200 = (struct dw200_subdev *)pdriver_dev->private;
memcpy(&pes_dw200_priv->dw200, pdw200, sizeof(pes_dw200_priv->dw200));
+ pm_runtime_get_sync(pdw200->dev);
+
common_dmabuf_heap_import_init(&pes_dw200_priv->heap_root, pdw200->dev);
pdw200->pheap_root = &pes_dw200_priv->heap_root;
pes_dw200_priv->dw200.pheap_root = &pes_dw200_priv->heap_root;
@@ -989,17 +1000,15 @@ static int vvcam_sys_reset_release(dw_clk_rst_t *dw_crg)
}
#define VVCAM_SYS_CLK_PREPARE(clk) \
- { \
- ret = clk_prepare_enable(clk); \
- if (ret) { \
- pr_err("failed to enable clk %px\n", clk); \
- return ret; \
+ do { \
+ if (clk_prepare_enable(clk)) { \
+ pr_err("Failed to enable clk %px\n", clk); \
} \
- }
+ } while (0)
-static int vvcam_sys_clk_prepare(dw_clk_rst_t *dw_crg)
+static int vvcam_sys_clk_config(dw_clk_rst_t *dw_crg)
{
- int ret;
+ int ret = 0;
long rate;
ret = clk_set_parent(dw_crg->aclk_mux, dw_crg->spll0_fout1);
@@ -1033,19 +1042,32 @@ static int vvcam_sys_clk_prepare(dw_clk_rst_t *dw_crg)
}
pr_info("DW set vi_dig_dw to %ldHZ\n", rate);
}
+ return 0;
+}
+static int vvcam_sys_clk_prepare(dw_clk_rst_t *dw_crg)
+{
+ int ret = 0;
VVCAM_SYS_CLK_PREPARE(dw_crg->aclk);
VVCAM_SYS_CLK_PREPARE(dw_crg->cfg_clk);
VVCAM_SYS_CLK_PREPARE(dw_crg->dw_aclk);
-
+ ret = win2030_tbu_power(dw_crg->dev, true);
+ if (ret) {
+ pr_err("%s: DW tbu power up failed\n", __func__);
+ return ret;
+ }
return 0;
}
-static int vvcam_clk_reset_fini(dw_clk_rst_t *dw_crg)
+static int vvcam_sys_clk_unprepare(dw_clk_rst_t *dw_crg)
{
- reset_control_assert(dw_crg->rstc_dwe);
- reset_control_assert(dw_crg->rstc_cfg);
- reset_control_assert(dw_crg->rstc_axi);
+ int ret = 0;
+ // tbu power down need enanle clk
+ ret = win2030_tbu_power(dw_crg->dev, false);
+ if (ret) {
+ pr_err("dw tbu power down failed\n");
+ return ret;
+ }
clk_disable_unprepare(dw_crg->dw_aclk);
clk_disable_unprepare(dw_crg->cfg_clk);
@@ -1054,6 +1076,14 @@ static int vvcam_clk_reset_fini(dw_clk_rst_t *dw_crg)
return 0;
}
+static int vvcam_reset_fini(dw_clk_rst_t *dw_crg)
+{
+ reset_control_assert(dw_crg->rstc_dwe);
+ reset_control_assert(dw_crg->rstc_cfg);
+ reset_control_assert(dw_crg->rstc_axi);
+ return 0;
+}
+
static int dewarp_release(struct inode *inode, struct file *file)
{
u32 reg = 0;
@@ -1119,7 +1149,7 @@ static int dewarp_release(struct inode *inode, struct file *file)
vivdw200_destroy_circle_queue(&(pdw200->dwe_circle_list));
vivdw200_destroy_circle_queue(&(pdw200->vse_circle_list));
#endif
-
+ pm_runtime_put(pdw200->dev);
pes_dw200_priv->pdriver_dev = NULL;
vfree(pes_dw200_priv);
@@ -1184,21 +1214,22 @@ static int es_dewarp_probe(struct platform_device *pdev)
return ret;
}
- ret = vvcam_sys_clk_prepare(&pdwe_dev->dw_crg);
+ ret = vvcam_sys_clk_config(&pdwe_dev->dw_crg);
if (ret) {
pr_err("%s: DW clk prepare failed\n", __func__);
return ret;
}
- ret = vvcam_sys_reset_release(&pdwe_dev->dw_crg);
+ pdwe_dev->dw_crg.dev = &pdev->dev;
+ ret = vvcam_sys_clk_prepare(&pdwe_dev->dw_crg);
if (ret) {
- pr_err("%s: DW reset release failed\n", __func__);
+ pr_err("%s: DW clk prepare failed\n", __func__);
return ret;
}
- ret = win2030_tbu_power(&pdev->dev, true);
+ ret = vvcam_sys_reset_release(&pdwe_dev->dw_crg);
if (ret) {
- pr_err("%s: DW tbu power up failed\n", __func__);
+ pr_err("%s: DW reset release failed\n", __func__);
return ret;
}
@@ -1339,18 +1370,24 @@ static int es_dewarp_probe(struct platform_device *pdev)
pdwe_dev->dw200_reset = debugfs_create_file(
debug_dw200_reset, 0644, NULL, pdwe_dev, &dw200_reset_fops);
+ /* The code below assumes runtime PM to be disabled. */
+ WARN_ON(pm_runtime_enabled(&pdev->dev));
+ pm_runtime_use_autosuspend(&pdev->dev);
+ pm_runtime_set_active(&pdev->dev);
+ pm_runtime_enable(&pdev->dev);
+
+ atomic_set(&pdriver_dev->trigger_atom, 0);
+ init_waitqueue_head(&pdriver_dev->trigger_wq);
+
devise_register_index++;
- // pr_info("exit %s\n", __func__);
return ret;
}
static int es_dewarp_remove(struct platform_device *pdev)
{
- int ret;
struct es_dewarp_driver_dev *pdriver_dev;
struct dw200_subdev *pdwe_dev;
- // pr_info("enter %s\n", __func__);
devise_register_index--;
pdriver_dev = platform_get_drvdata(pdev);
@@ -1365,15 +1402,92 @@ static int es_dewarp_remove(struct platform_device *pdev)
if (devise_register_index == 0) {
class_destroy(pdriver_dev->class);
}
- ret = win2030_tbu_power(&pdev->dev, false);
- if (ret) {
- pr_err("dw tbu power down failed\n");
- return -1;
+
+ vvcam_reset_fini(&pdwe_dev->dw_crg);
+
+ pm_runtime_dont_use_autosuspend(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
+ return 0;
+}
+
+static int dewarp_runtime_suspend(struct device *dev)
+{
+ struct es_dewarp_driver_dev *pdriver_dev = dev_get_drvdata(dev);
+ struct dw200_subdev *pdwe_dev;
+
+ pdwe_dev = (struct dw200_subdev *)pdriver_dev->private;
+ return vvcam_sys_clk_unprepare(&pdwe_dev->dw_crg);
+}
+
+static int dewarp_runtime_resume(struct device *dev)
+{
+ struct es_dewarp_driver_dev *pdriver_dev = dev_get_drvdata(dev);
+ struct dw200_subdev *pdwe_dev;
+
+ pdwe_dev = (struct dw200_subdev *)pdriver_dev->private;
+ return vvcam_sys_clk_prepare(&pdwe_dev->dw_crg);
+}
+
+static int dewarp_suspend(struct device *dev)
+{
+ struct es_dewarp_driver_dev *pdriver_dev = dev_get_drvdata(dev);
+ struct dw200_subdev *pdwe_dev;
+ int ret = 0;
+ pdriver_dev->suspended = 0;
+
+ pdwe_dev = (struct dw200_subdev *)pdriver_dev->private;
+
+ if (pm_runtime_status_suspended(dev)) {
+ return 0;
+ }
+
+ if (atomic_read(&pdriver_dev->trigger_atom)) {
+ //dw200 is working wait done
+ ret = wait_event_interruptible_timeout(
+ pdriver_dev->trigger_wq,
+ atomic_read(&pdriver_dev->trigger_atom) == 0,
+ ES_WAIT_TIMEOUT_MS);
+
+ if (ret == 0) {
+ pr_err("Error: %s process timeout %lu, ret:%d\n",
+ __func__, ES_WAIT_TIMEOUT_MS, ret);
+ atomic_dec(&pdriver_dev->trigger_atom);
+ ret = -ETIMEDOUT;
+ return ret;
+ } else if (ret < 0) {
+ pr_err("interrupted !!!\n");
+ atomic_dec(&pdriver_dev->trigger_atom);
+ ret = -ERESTARTSYS;
+ return ret;
+ }
+ }
+ pdriver_dev->suspended = 1;
+ return vvcam_sys_clk_unprepare(&pdwe_dev->dw_crg);
+}
+
+static int dewarp_resume(struct device *dev)
+{
+ struct es_dewarp_driver_dev *pdriver_dev = dev_get_drvdata(dev);
+ struct dw200_subdev *pdwe_dev;
+
+ pdwe_dev = (struct dw200_subdev *)pdriver_dev->private;
+
+ if (pm_runtime_status_suspended(dev)) {
+ return 0;
}
- vvcam_clk_reset_fini(&pdwe_dev->dw_crg);
+
+ if (pdriver_dev->suspended) {
+ return vvcam_sys_clk_prepare(&pdwe_dev->dw_crg);
+ }
+
return 0;
}
+static const struct dev_pm_ops dewarp_pm_ops = {
+ SET_RUNTIME_PM_OPS(dewarp_runtime_suspend, dewarp_runtime_resume, NULL)
+ SET_SYSTEM_SLEEP_PM_OPS(dewarp_suspend, dewarp_resume)
+};
+
#define DEV_NAME "dewarp-dri"
static const struct of_device_id dw200_of_id_table[] = {
{
@@ -1391,6 +1505,7 @@ static struct platform_driver viv_platform_driver = {
.owner = THIS_MODULE,
.name = DEV_NAME,
.of_match_table = dw200_of_id_table,
+ .pm = &dewarp_pm_ops,
},
};
module_platform_driver(viv_platform_driver);
--
2.47.0