bigocean turn off clocks in idle state

Turn off bigocean clocks when it is idle for predefined timout.
This change is to save the power when user holds onto
bigocean instance but are not actually playing any video.
This can happen with video pause or app bug. The solution is
based on a timer.

Bug: 243066068

Signed-off-by: Vinay Kalia <[email protected]>
Signed-off-by: Ruofei Ma <[email protected]>
Change-Id: Ib7a09de489b8443821f1ef6b2f57d32dd5d9740a
diff --git a/drivers/media/platform/bigocean/bigo.c b/drivers/media/platform/bigocean/bigo.c
index 4d1c7a7..f84923c 100644
--- a/drivers/media/platform/bigocean/bigo.c
+++ b/drivers/media/platform/bigocean/bigo.c
@@ -34,6 +34,7 @@
 #define DEFAULT_FPS 60
 #define BIGO_SMC_ID 0xd
 #define BIGO_MAX_INST_NUM 16
+#define BIGO_IDLE_TIMEOUT_MS 1000
 
 static struct sscd_platform_data bigo_sscd_platdata;
 
@@ -85,6 +86,14 @@ static inline int on_first_instance_open(struct bigo_core *core)
 	return rc;
 }
 
+void inactivity_detected(struct timer_list *t)
+{
+	struct bigo_core *core = from_timer(core, t, idle_timer);
+
+	if (core)
+		bigo_enter_idle_state(core);
+}
+
 static inline void on_last_inst_close(struct bigo_core *core)
 {
 #if IS_ENABLED(CONFIG_PM)
@@ -131,6 +140,7 @@ static int bigo_open(struct inode *inode, struct file *file)
 	inst->width = DEFAULT_HEIGHT;
 	inst->fps = DEFAULT_FPS;
 	inst->core = core;
+	inst->idle = true;
 	mutex_lock(&core->lock);
 	if (list_empty(&core->instances)) {
 		rc = on_first_instance_open(core);
@@ -145,7 +155,7 @@ static int bigo_open(struct inode *inode, struct file *file)
 		goto err;
 	}
 	mutex_unlock(&core->lock);
-	bigo_update_qos(core);
+	bigo_mark_qos_dirty(core);
 	pr_info("opened bigocean instance\n");
 
 	return 0;
@@ -172,7 +182,17 @@ static int bigo_release(struct inode *inode, struct file *file)
 	if (list_empty(&core->instances))
 		on_last_inst_close(core);
 	mutex_unlock(&core->lock);
+	bigo_mark_qos_dirty(core);
+
+	mutex_lock(&core->lock);
 	bigo_update_qos(core);
+
+	/* Set the timer at instance close to handle other inactive
+	 * instances if there is any */
+	mod_timer(&core->idle_timer,
+			jiffies + msecs_to_jiffies(BIGO_IDLE_TIMEOUT_MS));
+	mutex_unlock(&core->lock);
+
 	pr_info("closed bigocean instance\n");
 	return 0;
 }
@@ -183,6 +203,7 @@ static int bigo_run_job(struct bigo_core *core, struct bigo_job *job)
 	int rc = 0;
 	u32 status = 0;
 
+	bigo_update_qos(core);
 	bigo_bypass_ssmt_pid(core);
 	bigo_push_regs(core, job->regs);
 	bigo_core_enable(core);
@@ -210,6 +231,9 @@ static int bigo_run_job(struct bigo_core *core, struct bigo_job *job)
 	*(u32 *)(job->regs + BIGO_REG_STAT) = status;
 	if (rc || ret)
 		rc = -ETIMEDOUT;
+
+	mod_timer(&core->idle_timer,
+			jiffies + msecs_to_jiffies(BIGO_IDLE_TIMEOUT_MS));
 	return rc;
 }
 
@@ -264,7 +288,7 @@ inline void bigo_config_frmrate(struct bigo_inst *inst, __u32 frmrate)
 	mutex_lock(&inst->lock);
 	inst->fps = frmrate;
 	mutex_unlock(&inst->lock);
-	bigo_update_qos(inst->core);
+	bigo_mark_qos_dirty(inst->core);
 }
 
 inline void bigo_config_frmsize(struct bigo_inst *inst,
@@ -274,7 +298,7 @@ inline void bigo_config_frmsize(struct bigo_inst *inst,
 	inst->height = frmsize->height;
 	inst->width = frmsize->width;
 	mutex_unlock(&inst->lock);
-	bigo_update_qos(inst->core);
+	bigo_mark_qos_dirty(inst->core);
 }
 
 inline void bigo_config_secure(struct bigo_inst *inst, __u32 is_secure)
@@ -326,6 +350,13 @@ static long bigo_unlocked_ioctl(struct file *file, unsigned int cmd,
 			}
 		}
 
+		if (is_idle(inst)) {
+			mutex_lock(&inst->lock);
+			inst->idle = false;
+			mutex_unlock(&inst->lock);
+			core->qos_dirty = true;
+		}
+
 		rc = bigo_process(core, &desc);
 		if (rc)
 			pr_err("Error processing data: %d\n", rc);
@@ -499,6 +530,7 @@ static int bigo_probe(struct platform_device *pdev)
 	INIT_LIST_HEAD(&core->pm.bw);
 	spin_lock_init(&core->status_lock);
 	init_completion(&core->frame_done);
+	timer_setup(&core->idle_timer, inactivity_detected, 0);
 	core->dev = &pdev->dev;
 	platform_set_drvdata(pdev, core);
 
@@ -551,6 +583,7 @@ static int bigo_probe(struct platform_device *pdev)
 	deinit_chardev(core);
 err_init_chardev:
 	platform_set_drvdata(pdev, NULL);
+	del_timer(&core->idle_timer);
 err:
 	return rc;
 }
@@ -559,6 +592,7 @@ static int bigo_remove(struct platform_device *pdev)
 {
 	struct bigo_core *core = (struct bigo_core *)platform_get_drvdata(pdev);
 
+	del_timer_sync(&core->idle_timer);
 	bigo_uninit_debugfs(core);
 	platform_device_unregister(&bigo_sscd_dev);
 	bigo_pt_client_unregister(core);
diff --git a/drivers/media/platform/bigocean/bigo_pm.c b/drivers/media/platform/bigocean/bigo_pm.c
index 25fc7aa..b524b9b 100644
--- a/drivers/media/platform/bigocean/bigo_pm.c
+++ b/drivers/media/platform/bigocean/bigo_pm.c
@@ -17,6 +17,17 @@
 #include "bigo_pm.h"
 #include "bigo_io.h"
 
+bool is_idle(struct bigo_inst *inst)
+{
+	bool idle;
+
+	mutex_lock(&inst->lock);
+	idle = inst->idle;
+	mutex_unlock(&inst->lock);
+
+	return idle;
+}
+
 static inline u32 bigo_get_total_load(struct bigo_core *core)
 {
 	struct bigo_inst *inst;
@@ -27,6 +38,8 @@ static inline u32 bigo_get_total_load(struct bigo_core *core)
 		return 0;
 
 	list_for_each_entry(inst, &core->instances, list) {
+		if (is_idle(inst))
+			continue;
 		curr_load = inst->width * inst->height * inst->fps / 1024;
 		if (curr_load < core->pm.max_load - load) {
 			load += curr_load;
@@ -105,13 +118,51 @@ void bigo_update_qos(struct bigo_core *core)
 {
 	int rc;
 
+	if (core->qos_dirty) {
+		rc = bigo_scale_bw(core);
+
+		if (rc)
+			pr_warn("%s: failed to scale bandwidth: %d\n",
+					 __func__, rc);
+
+		bigo_scale_freq(core);
+
+		core->qos_dirty = false;
+	}
+}
+
+static inline void mark_instances_idle(struct bigo_core *core)
+{
+	struct bigo_inst *inst;
+	list_for_each_entry(inst, &core->instances, list) {
+		mutex_lock(&inst->lock);
+		inst->idle = true;
+		mutex_unlock(&inst->lock);
+	}
+}
+
+static void bigo_clocks_off(struct bigo_core *core)
+{
+	struct bts_bw bw;
+	memset(&bw, 0, sizeof(struct bts_bw));
+	bts_update_bw(core->pm.bwindex, bw);
+	bigo_set_freq(core, bigo_get_target_freq(core, 0));
+}
+
+void bigo_enter_idle_state(struct bigo_core *core)
+{
 	mutex_lock(&core->lock);
-	rc = bigo_scale_bw(core);
+	bigo_clocks_off(core);
+	core->qos_dirty = true;
+	mark_instances_idle(core);
+	pr_info("bigocean entered idle state");
+	mutex_unlock(&core->lock);
+}
 
-	if (rc)
-		pr_warn("%s: failed to scale bandwidth: %d\n", __func__, rc);
-
-	bigo_scale_freq(core);
+void bigo_mark_qos_dirty(struct bigo_core *core)
+{
+	mutex_lock(&core->lock);
+	core->qos_dirty = true;
 	mutex_unlock(&core->lock);
 }
 
diff --git a/drivers/media/platform/bigocean/bigo_pm.h b/drivers/media/platform/bigocean/bigo_pm.h
index c571b87..c12ee9b 100644
--- a/drivers/media/platform/bigocean/bigo_pm.h
+++ b/drivers/media/platform/bigocean/bigo_pm.h
@@ -21,5 +21,8 @@ int bigo_runtime_suspend(struct device *dev);
 int bigo_runtime_resume(struct device *dev);
 #endif
 void bigo_update_qos(struct bigo_core *core);
+void bigo_enter_idle_state(struct bigo_core *core);
+void bigo_mark_qos_dirty(struct bigo_core *core);
+bool is_idle(struct bigo_inst *inst);
 
 #endif //_BIGO_PM_H_
diff --git a/drivers/media/platform/bigocean/bigo_priv.h b/drivers/media/platform/bigocean/bigo_priv.h
index 649dbc6..4ac69a2 100644
--- a/drivers/media/platform/bigocean/bigo_priv.h
+++ b/drivers/media/platform/bigocean/bigo_priv.h
@@ -101,6 +101,8 @@ struct bigo_core {
 	phys_addr_t paddr;
 	struct bigo_debugfs debugfs;
 	spinlock_t status_lock;
+	struct timer_list idle_timer;
+	u32 qos_dirty;
 };
 
 struct bigo_inst {
@@ -117,6 +119,7 @@ struct bigo_inst {
 	struct bigo_bw pk_bw[AVG_CNT];
 	int job_cnt;
 	u32 hw_cycles[AVG_CNT];
+	bool idle;
 };
 
 inline void set_curr_inst(struct bigo_core *core, struct bigo_inst *inst);