bigocean: Pull in lower-clocks-during-idle changes from latest

Pull in the changes to lower the clocks when there is no traffic from
the latest codebase. Changes previously added to achieve the same
resulted in stability issues. So, instead of adding those new changes
pull in already tested changes on gs101 from the latest.

bug: 243066068

Signed-off-by: Vinay Kalia <vinaykalia@google.com>
Change-Id: I476e3851481d8d5db0fbea5da014c0bd57d93a07
diff --git a/drivers/media/platform/bigocean/Makefile b/drivers/media/platform/bigocean/Makefile
index 9bc7a21..cbac244 100644
--- a/drivers/media/platform/bigocean/Makefile
+++ b/drivers/media/platform/bigocean/Makefile
@@ -1,4 +1,4 @@
 obj-$(CONFIG_BIGOCEAN) += bigocean.o
-bigocean-$(CONFIG_BIGOCEAN) += bigo.o bigo_pm.o bigo_io.o bigo_of.o bigo_iommu.o
+bigocean-$(CONFIG_BIGOCEAN) += bigo.o bigo_pm.o bigo_io.o bigo_of.o bigo_iommu.o bigo_prioq.o
 bigocean-$(CONFIG_SLC_PARTITION_MANAGER) += bigo_slc.o
 bigocean-$(CONFIG_DEBUG_FS) += bigo_debug.o
diff --git a/drivers/media/platform/bigocean/bigo.c b/drivers/media/platform/bigocean/bigo.c
index f84923c..2b75d3e 100644
--- a/drivers/media/platform/bigocean/bigo.c
+++ b/drivers/media/platform/bigocean/bigo.c
@@ -17,6 +17,7 @@
 #include <linux/uaccess.h>
 #include <linux/platform_data/sscoredump.h>
 #include <linux/soc/samsung/exynos-smc.h>
+#include <linux/kthread.h>
 
 #include "bigo_io.h"
 #include "bigo_iommu.h"
@@ -25,6 +26,7 @@
 #include "bigo_priv.h"
 #include "bigo_slc.h"
 #include "bigo_debug.h"
+#include "bigo_prioq.h"
 
 #define BIGO_DEVCLASS_NAME "video_codec"
 #define BIGO_CHRDEV_NAME "bigocean"
@@ -34,8 +36,11 @@
 #define DEFAULT_FPS 60
 #define BIGO_SMC_ID 0xd
 #define BIGO_MAX_INST_NUM 16
+
 #define BIGO_IDLE_TIMEOUT_MS 1000
 
+static int bigo_worker_thread(void *data);
+
 static struct sscd_platform_data bigo_sscd_platdata;
 
 static void bigo_sscd_release(struct device *dev)
@@ -74,26 +79,35 @@
 
 static inline int on_first_instance_open(struct bigo_core *core)
 {
-	int rc = bigo_pt_client_enable(core);
+	int rc;
 
-	if (rc)
+	core->worker_thread = kthread_run(bigo_worker_thread, (void *)core,
+					"bigo_worker_thread");
+	if (IS_ERR(core->worker_thread)) {
+		rc = PTR_ERR(core->worker_thread);
+		core->worker_thread = NULL;
+		pr_err("failed to create worker thread rc = %d\n", rc);
+		goto exit;
+	}
+
+	rc = bigo_pt_client_enable(core);
+	if (rc) {
 		pr_info("failed to enable SLC");
+		kthread_stop(core->worker_thread);
+		goto exit;
+	}
 #if IS_ENABLED(CONFIG_PM)
 	rc = pm_runtime_get_sync(core->dev);
-	if (rc)
+	if (rc) {
 		pr_err("failed to resume: %d\n", rc);
+		kthread_stop(core->worker_thread);
+	}
 #endif
+
+exit:
 	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)
@@ -101,11 +115,9 @@
 		pr_warn("failed to suspend\n");
 #endif
 	bigo_pt_client_disable(core);
-	kfree(core->job.regs);
-	core->job.regs = NULL;
 }
 
-static inline int bigo_add_inst(struct bigo_inst *inst, struct bigo_core *core)
+static inline int bigo_count_inst(struct bigo_core *core)
 {
 	int count = 0;
 	struct list_head *pos;
@@ -113,11 +125,7 @@
 	list_for_each(pos, &core->instances)
 		count++;
 
-	if (count >= BIGO_MAX_INST_NUM)
-		return -ENOMEM;
-
-	list_add_tail(&inst->list, &core->instances);
-	return 0;
+	return count;
 }
 
 static int bigo_open(struct inode *inode, struct file *file)
@@ -130,70 +138,87 @@
 	if (!inst) {
 		rc = -ENOMEM;
 		pr_err("Failed to create instance\n");
-		return rc;
+		goto err;
 	}
 	INIT_LIST_HEAD(&inst->list);
 	INIT_LIST_HEAD(&inst->buffers);
+	kref_init(&inst->refcount);
 	mutex_init(&inst->lock);
+	init_completion(&inst->job_comp);
 	file->private_data = inst;
 	inst->height = DEFAULT_WIDTH;
 	inst->width = DEFAULT_HEIGHT;
 	inst->fps = DEFAULT_FPS;
 	inst->core = core;
 	inst->idle = true;
+	inst->job.regs_size = core->regs_size;
+	inst->job.regs = kzalloc(core->regs_size, GFP_KERNEL);
+	if (!inst->job.regs) {
+		rc = -ENOMEM;
+		pr_err("Failed to alloc job regs\n");
+		goto err_first_inst;
+	}
 	mutex_lock(&core->lock);
+	if (bigo_count_inst(core) >= BIGO_MAX_INST_NUM) {
+		rc = -ENOMEM;
+		pr_err("Reaches max number of supported instances\n");
+		mutex_unlock(&core->lock);
+		goto err_inst_open;
+	}
 	if (list_empty(&core->instances)) {
 		rc = on_first_instance_open(core);
 		if (rc) {
 			pr_err("failed to setup first instance");
-			goto err;
+			mutex_unlock(&core->lock);
+			goto err_inst_open;
 		}
 	}
-	rc = bigo_add_inst(inst, core);
-	if (rc) {
-		pr_err("Reaches max number of supported instances\n");
-		goto err;
-	}
+	list_add_tail(&inst->list, &core->instances);
 	mutex_unlock(&core->lock);
 	bigo_mark_qos_dirty(core);
-	pr_info("opened bigocean instance\n");
-
-	return 0;
-err:
-	mutex_unlock(&core->lock);
-	kfree(inst);
+	pr_info("opened instance\n");
 	return rc;
+
+err_inst_open:
+	kfree(inst->job.regs);
+err_first_inst:
+	kfree(inst);
+err:
+	return rc;
+}
+
+static void bigo_close(struct kref *ref)
+{
+	struct bigo_inst *inst = container_of(ref, struct bigo_inst, refcount);
+
+	if (inst && inst->core) {
+		clear_job_from_prioq(inst->core, inst);
+		bigo_unmap_all(inst);
+		bigo_mark_qos_dirty(inst->core);
+		bigo_update_qos(inst->core);
+		kfree(inst->job.regs);
+		kfree(inst);
+		pr_info("closed instance\n");
+	}
 }
 
 static int bigo_release(struct inode *inode, struct file *file)
 {
-	struct bigo_core *core =
-		container_of(inode->i_cdev, struct bigo_core, cdev);
 	struct bigo_inst *inst = file->private_data;
+	struct bigo_core *core = inst->core;
 
-	if (!inst || !core) {
-		pr_err("No instance or core\n");
+	if (!inst || !core)
 		return -EINVAL;
-	}
-	bigo_unmap_all(inst);
+
 	mutex_lock(&core->lock);
 	list_del(&inst->list);
-	kfree(inst);
-	if (list_empty(&core->instances))
+	if (list_empty(&core->instances)) {
+		kthread_stop(core->worker_thread);
 		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");
+	kref_put(&inst->refcount, bigo_close);
 	return 0;
 }
 
@@ -202,8 +227,8 @@
 	long ret = 0;
 	int rc = 0;
 	u32 status = 0;
+	unsigned long flags;
 
-	bigo_update_qos(core);
 	bigo_bypass_ssmt_pid(core);
 	bigo_push_regs(core, job->regs);
 	bigo_core_enable(core);
@@ -211,6 +236,11 @@
 			msecs_to_jiffies(JOB_COMPLETE_TIMEOUT_MS));
 	if (!ret) {
 		pr_err("timed out waiting for HW\n");
+
+		spin_lock_irqsave(&core->status_lock, flags);
+		core->stat_with_irq = bigo_core_readl(core, BIGO_REG_STAT);
+		spin_unlock_irqrestore(&core->status_lock, flags);
+
 		bigo_core_disable(core);
 		rc = -ETIMEDOUT;
 	} else {
@@ -231,55 +261,6 @@
 	*(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;
-}
-
-static int bigo_process(struct bigo_core *core, struct bigo_ioc_regs *desc)
-{
-	int rc = 0;
-	struct bigo_job *job = &core->job;
-
-	if (!desc) {
-		pr_err("Invalid input\n");
-		return -EINVAL;
-	}
-	if (desc->regs_size != core->regs_size) {
-		pr_err("Register size passed from userspace(%u) is different(%u)\n",
-		       (unsigned int)desc->regs_size, core->regs_size);
-		return -EINVAL;
-	}
-
-	if (!job->regs) {
-		job->regs = kzalloc(core->regs_size, GFP_KERNEL);
-		if (!job->regs) {
-			rc = -ENOMEM;
-			goto exit;
-		}
-	}
-
-	if (copy_from_user(job->regs, (void *)desc->regs, core->regs_size)) {
-		pr_err("Failed to copy from user\n");
-		rc = -EFAULT;
-		goto exit;
-	}
-
-	/*TODO(vinaykalia@): Replace this with EDF scheduler.*/
-	rc = bigo_run_job(core, job);
-	if (rc) {
-		pr_err("Error running job\n");
-		goto exit;
-	}
-
-	if (copy_to_user((void *)desc->regs, job->regs, core->regs_size)) {
-		pr_err("Failed to copy to user\n");
-		rc = -EFAULT;
-		goto exit;
-	}
-
-exit:
 	return rc;
 }
 
@@ -308,6 +289,41 @@
 	mutex_unlock(&inst->lock);
 }
 
+static int copy_regs_from_user(struct bigo_core *core,
+			struct bigo_ioc_regs *desc,
+			void __user *user_desc,
+			struct bigo_job *job)
+{
+	if (!core || !desc || !user_desc || !job)
+		return -EINVAL;
+
+	if (copy_from_user(desc, user_desc, sizeof(*desc)))
+		return -EFAULT;
+
+	if (desc->regs_size != core->regs_size) {
+		pr_err("Reg size of userspace(%u) is different(%u)\n",
+		(unsigned int)desc->regs_size, core->regs_size);
+		return -EINVAL;
+	}
+
+	if (copy_from_user(job->regs, (void *)desc->regs, desc->regs_size))
+		return -EFAULT;
+
+	return 0;
+}
+
+static int copy_regs_to_user(struct bigo_ioc_regs *desc,
+				struct bigo_job *job)
+{
+	if (!desc || !job)
+		return -EINVAL;
+
+	if (copy_to_user((void *)desc->regs, job->regs, desc->regs_size))
+		return -EFAULT;
+
+	return 0;
+}
+
 static long bigo_unlocked_ioctl(struct file *file, unsigned int cmd,
 				unsigned long arg)
 {
@@ -315,10 +331,11 @@
 	struct bigo_core *core =
 		container_of(file->f_inode->i_cdev, struct bigo_core, cdev);
 	void __user *user_desc = (void __user *)arg;
-	struct bigo_ioc_regs desc;
 	struct bigo_ioc_mapping mapping;
 	struct bigo_ioc_frmsize frmsize;
 	struct bigo_cache_info cinfo;
+	struct bigo_inst *curr_inst;
+	bool found = false;
 	int rc = 0;
 
 	if (_IOC_TYPE(cmd) != BIGO_IOC_MAGIC) {
@@ -333,42 +350,59 @@
 		pr_err("No instance or core\n");
 		return -EINVAL;
 	}
+	mutex_lock(&core->lock);
+	list_for_each_entry(curr_inst, &core->instances, list) {
+		if (curr_inst == inst) {
+			found = true;
+			break;
+		}
+	}
+
+	if (!found) {
+		mutex_unlock(&core->lock);
+		pr_err("this instance is invalid");
+		return -EINVAL;
+	}
+	kref_get(&inst->refcount);
+	mutex_unlock(&core->lock);
 	switch (cmd) {
 	case BIGO_IOCX_PROCESS:
-		if (copy_from_user(&desc, user_desc, sizeof(desc))) {
-			pr_err("Failed to copy from user\n");
+	{
+		struct bigo_ioc_regs desc;
+		struct bigo_job *job = &inst->job;
+		long ret;
+
+		if (copy_regs_from_user(core, &desc, user_desc, job)) {
+			pr_err("Failed to copy regs from user\n");
 			return -EFAULT;
 		}
-		mutex_lock(&core->lock);
-		if (inst->is_secure) {
-			rc = exynos_smc(SMC_PROTECTION_SET, 0, BIGO_SMC_ID,
-					SMC_PROTECTION_ENABLE);
-			if (rc) {
-				pr_err("failed to enable SMC_PROTECTION_SET: %d\n", rc);
-				mutex_unlock(&core->lock);
-				break;
-			}
+
+		if(enqueue_prioq(core, inst)) {
+			pr_err("Failed enqueue frame\n");
+			return -EFAULT;
 		}
 
-		if (is_idle(inst)) {
-			mutex_lock(&inst->lock);
-			inst->idle = false;
-			mutex_unlock(&inst->lock);
-			core->qos_dirty = true;
+		ret = wait_for_completion_timeout(
+			&inst->job_comp,
+			msecs_to_jiffies(JOB_COMPLETE_TIMEOUT_MS * 16));
+		if (!ret) {
+			pr_err("timed out waiting for HW: %d\n", rc);
+			clear_job_from_prioq(core, inst);
+			rc = -ETIMEDOUT;
+		} else {
+			rc = (ret > 0) ? 0 : ret;
 		}
 
-		rc = bigo_process(core, &desc);
 		if (rc)
-			pr_err("Error processing data: %d\n", rc);
+			break;
 
-		if (inst->is_secure) {
-			rc = exynos_smc(SMC_PROTECTION_SET, 0, BIGO_SMC_ID,
-					SMC_PROTECTION_DISABLE);
-			if (rc)
-				pr_err("failed to disable SMC_PROTECTION_SET: %d\n", rc);
+		rc = job->status;
+		if(copy_regs_to_user(&desc, job)) {
+			pr_err("Failed to copy regs to user\n");
+			rc = -EFAULT;
 		}
-		mutex_unlock(&core->lock);
 		break;
+	}
 	case BIGO_IOCX_MAP:
 		if (copy_from_user(&mapping, user_desc, sizeof(mapping))) {
 			pr_err("Failed to copy from user\n");
@@ -423,6 +457,7 @@
 		break;
 	}
 
+	kref_put(&inst->refcount, bigo_close);
 	return rc;
 }
 
@@ -513,9 +548,90 @@
 	unregister_chrdev_region(core->devno, 1);
 }
 
+static inline void mark_instances_idle(struct bigo_core *core)
+{
+	struct bigo_inst *curr_inst;
+	mutex_lock(&core->lock);
+	list_for_each_entry(curr_inst, &core->instances, list)
+		curr_inst->idle = true;
+	mutex_unlock(&core->lock);
+}
+
+static int bigo_worker_thread(void *data)
+{
+	struct bigo_core *core = (struct bigo_core *)data;
+	struct bigo_inst *inst;
+	struct bigo_job *job = NULL;
+	bool should_stop;
+	int rc;
+
+	if (!core)
+		return -ENOMEM;
+
+	while(1) {
+		rc = wait_event_timeout(core->worker,
+			dequeue_prioq(core, &job, &should_stop),
+			msecs_to_jiffies(BIGO_IDLE_TIMEOUT_MS));
+		if (!rc && !should_stop) {
+			/* Mark all instances as IDLE since none of these
+			 * instances queued a job for BIGO_IDLE_TIMEOUT_MS
+			 */
+			mark_instances_idle(core);
+			bigo_clocks_off(core);
+			bigo_mark_qos_dirty(core);
+			pr_info("bigocean entered idle state\n");
+			wait_event(core->worker,
+				dequeue_prioq(core, &job, &should_stop));
+			pr_info("bigocean resumed to work\n");
+		}
+		if(should_stop) {
+			pr_info("worker thread received stop signal, exit\n");
+			return 0;
+		}
+		if (!job)
+			continue;
+
+		inst = container_of(job, struct bigo_inst, job);
+
+		if (inst->idle) {
+			inst->idle = false;
+			bigo_mark_qos_dirty(core);
+		}
+
+		bigo_update_qos(core);
+		if (inst->is_secure) {
+			rc = exynos_smc(SMC_PROTECTION_SET, 0, BIGO_SMC_ID,
+					SMC_PROTECTION_ENABLE);
+			if (rc) {
+				pr_err("failed to enable SMC_PROTECTION_SET: %d\n", rc);
+				goto done;
+			}
+		}
+
+		rc = bigo_run_job(core, job);
+		if (rc) {
+			pr_err("Error running job\n");
+			goto done;
+		}
+
+		if (inst->is_secure) {
+			rc = exynos_smc(SMC_PROTECTION_SET, 0, BIGO_SMC_ID,
+					SMC_PROTECTION_DISABLE);
+			if (rc)
+				pr_err("failed to disable SMC_PROTECTION_SET: %d\n", rc);
+		}
+
+	done:
+		job->status = rc;
+		complete(&inst->job_comp);
+	}
+	return 0;
+}
+
 static int bigo_probe(struct platform_device *pdev)
 {
 	int rc = 0;
+	int i;
 	struct bigo_core *core;
 
 	core = devm_kzalloc(&pdev->dev, sizeof(struct bigo_core), GFP_KERNEL);
@@ -525,12 +641,15 @@
 	}
 
 	mutex_init(&core->lock);
+	mutex_init(&core->prioq.lock);
 	INIT_LIST_HEAD(&core->instances);
 	INIT_LIST_HEAD(&core->pm.opps);
 	INIT_LIST_HEAD(&core->pm.bw);
+	for(i = 0; i < BO_MAX_PRIO; ++i)
+		INIT_LIST_HEAD(&core->prioq.queue[i]);
 	spin_lock_init(&core->status_lock);
 	init_completion(&core->frame_done);
-	timer_setup(&core->idle_timer, inactivity_detected, 0);
+	init_waitqueue_head(&core->worker);
 	core->dev = &pdev->dev;
 	platform_set_drvdata(pdev, core);
 
@@ -566,7 +685,13 @@
 		goto err_fault_handler;
 	}
 
-	bigo_pt_client_register(pdev->dev.of_node, core);
+	rc = bigo_pt_client_register(pdev->dev.of_node, core);
+	if (rc == -EPROBE_DEFER) {
+		pr_warn("pt_client returns -EPROBE_DEFER, try again later\n");
+		goto err_pt_client;
+	} else {
+		rc = 0;
+	}
 
 	if(platform_device_register(&bigo_sscd_dev))
 		pr_warn("Failed to register bigo_sscd_dev.\n");
@@ -575,6 +700,8 @@
 
 	return rc;
 
+err_pt_client:
+	iommu_unregister_device_fault_handler(&pdev->dev);
 err_fault_handler:
 	pm_runtime_disable(&pdev->dev);
 err_io:
@@ -583,7 +710,6 @@
 	deinit_chardev(core);
 err_init_chardev:
 	platform_set_drvdata(pdev, NULL);
-	del_timer(&core->idle_timer);
 err:
 	return rc;
 }
@@ -592,7 +718,6 @@
 {
 	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 b524b9b..60c5299 100644
--- a/drivers/media/platform/bigocean/bigo_pm.c
+++ b/drivers/media/platform/bigocean/bigo_pm.c
@@ -17,17 +17,6 @@
 #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;
@@ -38,7 +27,7 @@
 		return 0;
 
 	list_for_each_entry(inst, &core->instances, list) {
-		if (is_idle(inst))
+		if (inst->idle)
 			continue;
 		curr_load = inst->width * inst->height * inst->fps / 1024;
 		if (curr_load < core->pm.max_load - load) {
@@ -98,11 +87,14 @@
 static void bigo_get_bw(struct bigo_core *core, struct bts_bw *bw)
 {
 	u32 load = bigo_get_total_load(core);
-	struct bigo_bw *bandwidth = bigo_get_target_bw(core, load);
-
-	bw->read = bandwidth->rd_bw;
-	bw->write = bandwidth->wr_bw;
-	bw->peak = bandwidth->pk_bw;
+	if (load) {
+		struct bigo_bw *bandwidth = bigo_get_target_bw(core, load);
+		bw->read = bandwidth->rd_bw;
+		bw->write = bandwidth->wr_bw;
+		bw->peak = bandwidth->pk_bw;
+	} else {
+		memset(bw, 0, sizeof(*bw));
+	}
 	pr_debug("BW: load: %u, rd: %u, wr: %u, pk: %u", load, bw->read, bw->write, bw->peak);
 }
 
@@ -114,51 +106,6 @@
 	return bts_update_bw(core->pm.bwindex, bw);
 }
 
-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);
-	bigo_clocks_off(core);
-	core->qos_dirty = true;
-	mark_instances_idle(core);
-	pr_info("bigocean entered idle state");
-	mutex_unlock(&core->lock);
-}
-
 void bigo_mark_qos_dirty(struct bigo_core *core)
 {
 	mutex_lock(&core->lock);
@@ -166,6 +113,34 @@
 	mutex_unlock(&core->lock);
 }
 
+void bigo_update_qos(struct bigo_core *core)
+{
+	int rc;
+
+	mutex_lock(&core->lock);
+	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;
+	}
+	mutex_unlock(&core->lock);
+}
+
+void bigo_clocks_off(struct bigo_core *core)
+{
+	struct bts_bw bw;
+
+	memset(&bw, 0, sizeof(struct bts_bw));
+
+	mutex_lock(&core->lock);
+	bts_update_bw(core->pm.bwindex, bw);
+	bigo_set_freq(core, bigo_get_target_freq(core, 0));
+	mutex_unlock(&core->lock);
+}
+
 /*
  * bigo_pm_init(): Initializes power management for bigocean.
  * @core: the bigocean core
diff --git a/drivers/media/platform/bigocean/bigo_pm.h b/drivers/media/platform/bigocean/bigo_pm.h
index c12ee9b..50064a5 100644
--- a/drivers/media/platform/bigocean/bigo_pm.h
+++ b/drivers/media/platform/bigocean/bigo_pm.h
@@ -21,8 +21,7 @@
 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_clocks_off(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_prioq.c b/drivers/media/platform/bigocean/bigo_prioq.c
new file mode 100644
index 0000000..c6e36ef
--- /dev/null
+++ b/drivers/media/platform/bigocean/bigo_prioq.c
@@ -0,0 +1,82 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Priority queue impletation with BigOcean
+ *
+ * Copyright 2021 Google LLC.
+ *
+ * Author: Ruofei Ma <ruofeim@google.com>
+ */
+
+#include <linux/kthread.h>
+#include <linux/module.h>
+
+#include "bigo_prioq.h"
+
+int enqueue_prioq(struct bigo_core *core, struct bigo_inst *inst)
+{
+	struct bigo_job *job = &inst->job;
+
+	if(!core || !inst)
+		return -EINVAL;
+
+	mutex_lock(&core->prioq.lock);
+	list_add_tail(&job->list, &core->prioq.queue[inst->priority]);
+	set_bit(inst->priority, &core->prioq.bitmap);
+	mutex_unlock(&core->prioq.lock);
+
+	wake_up(&core->worker);
+	return 0;
+}
+
+bool dequeue_prioq(struct bigo_core *core, struct bigo_job **job,
+			bool *should_stop)
+{
+	int high_prio;
+	struct bigo_job *j = NULL;
+	struct list_head *queue;
+	if (!core || !job || !should_stop)
+		return false;
+
+	*should_stop = false;
+	if(kthread_should_stop()) {
+		*should_stop = true;
+		return true;
+	}
+
+	mutex_lock(&core->prioq.lock);
+	high_prio = ffs(core->prioq.bitmap) - 1;
+	if (high_prio < 0)
+		goto exit;
+
+	queue = &core->prioq.queue[high_prio];
+	j = list_first_entry_or_null(queue, struct bigo_job, list);
+	if (j) {
+		list_del(&j->list);
+		if (list_empty(queue))
+			clear_bit(high_prio, &core->prioq.bitmap);
+	}
+
+exit:
+	mutex_unlock(&core->prioq.lock);
+	*job = j;
+	return *job != NULL;
+}
+
+void clear_job_from_prioq(struct bigo_core *core, struct bigo_inst *inst)
+{
+	int i;
+	struct bigo_job *curr, *next;
+	struct bigo_inst *curr_inst;
+	mutex_lock(&core->prioq.lock);
+	for (i = 0; i < BO_MAX_PRIO; i++) {
+		list_for_each_entry_safe(curr, next, &core->prioq.queue[i], list) {
+			curr_inst = container_of(curr, struct bigo_inst, job);
+			if (inst == curr_inst)
+				list_del(&curr->list);
+		}
+	}
+	mutex_unlock(&core->prioq.lock);
+}
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Ruofei Ma <ruofeim@google.com>");
diff --git a/drivers/media/platform/bigocean/bigo_prioq.h b/drivers/media/platform/bigocean/bigo_prioq.h
new file mode 100644
index 0000000..dcd100c
--- /dev/null
+++ b/drivers/media/platform/bigocean/bigo_prioq.h
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright 2021 Google LLC.
+ *
+ * Author: Ruofei Ma <ruofeim@google.com>
+ */
+
+#ifndef _BIGO_PRIOQ_H_
+#define _BIGO_PRIOQ_H_
+
+#include "bigo_priv.h"
+
+bool dequeue_prioq(struct bigo_core *core, struct bigo_job **job,
+		bool *should_stop);
+int enqueue_prioq(struct bigo_core *core, struct bigo_inst *inst);
+
+void clear_job_from_prioq(struct bigo_core *core, struct bigo_inst *inst);
+
+#endif //_BIGO_PRIOQ_H_
diff --git a/drivers/media/platform/bigocean/bigo_priv.h b/drivers/media/platform/bigocean/bigo_priv.h
index 4ac69a2..6d07cf1 100644
--- a/drivers/media/platform/bigocean/bigo_priv.h
+++ b/drivers/media/platform/bigocean/bigo_priv.h
@@ -25,6 +25,7 @@
 #define AVG_CNT 30
 #define PEAK_CNT 5
 #define BUS_WIDTH 16
+#define BO_MAX_PRIO 2
 
 struct bufinfo {
 	struct list_head list;
@@ -69,8 +70,10 @@
 };
 
 struct bigo_job {
+	struct list_head list;
 	void *regs;
 	size_t regs_size;
+	int status;
 };
 
 struct bigo_debugfs {
@@ -79,6 +82,12 @@
 	u32 trigger_ssr;
 };
 
+struct bigo_prio_array {
+	struct mutex lock;
+	unsigned long bitmap;
+	struct list_head queue[BO_MAX_PRIO];
+};
+
 struct bigo_core {
 	struct class *_class;
 	struct cdev cdev;
@@ -93,7 +102,6 @@
 	struct list_head instances;
 	struct ion_client *mem_client;
 	u32 stat_with_irq;
-	struct bigo_job job;
 	struct power_manager pm;
 	struct slc_manager slc;
 	unsigned int regs_size;
@@ -101,7 +109,9 @@
 	phys_addr_t paddr;
 	struct bigo_debugfs debugfs;
 	spinlock_t status_lock;
-	struct timer_list idle_timer;
+	struct task_struct *worker_thread;
+	wait_queue_head_t worker;
+	struct bigo_prio_array prioq;
 	u32 qos_dirty;
 };
 
@@ -110,15 +120,19 @@
 	struct list_head buffers;
 	/* mutex protecting this data structure */
 	struct mutex lock;
+	struct kref refcount;
 	struct bigo_core *core;
 	u32 height;
 	u32 width;
 	u32 fps;
 	u32 is_secure;
+	int priority;
 	struct bigo_bw avg_bw[AVG_CNT];
 	struct bigo_bw pk_bw[AVG_CNT];
 	int job_cnt;
 	u32 hw_cycles[AVG_CNT];
+	struct completion job_comp;
+	struct bigo_job job;
 	bool idle;
 };
 
diff --git a/drivers/media/platform/bigocean/bigo_slc.c b/drivers/media/platform/bigocean/bigo_slc.c
index 744d77c..61d8c19 100644
--- a/drivers/media/platform/bigocean/bigo_slc.c
+++ b/drivers/media/platform/bigocean/bigo_slc.c
@@ -79,13 +79,17 @@
 	core->slc.size = size_allocated;
 }
 
-void bigo_pt_client_register(struct device_node *node, struct bigo_core *core)
+int bigo_pt_client_register(struct device_node *node, struct bigo_core *core)
 {
+	int rc = 0;
 	core->slc.pt_hnd = pt_client_register(node, (void *)core, bigo_pt_resize_cb);
 	if (IS_ERR(core->slc.pt_hnd)) {
+		rc = PTR_ERR(core->slc.pt_hnd);
 		core->slc.pt_hnd = NULL;
 		pr_warn("Failed to register pt_client.\n");
+		return rc;
 	}
+	return rc;
 }
 
 void bigo_pt_client_unregister(struct bigo_core *core)
diff --git a/drivers/media/platform/bigocean/bigo_slc.h b/drivers/media/platform/bigocean/bigo_slc.h
index cf9c1df..16e4110 100644
--- a/drivers/media/platform/bigocean/bigo_slc.h
+++ b/drivers/media/platform/bigocean/bigo_slc.h
@@ -12,14 +12,14 @@
 
 #if IS_ENABLED(CONFIG_SLC_PARTITION_MANAGER)
 #include <soc/google/pt.h>
-void bigo_pt_client_register(struct device_node *node, struct bigo_core *core);
+int bigo_pt_client_register(struct device_node *node, struct bigo_core *core);
 void bigo_pt_client_unregister(struct bigo_core *core);
 int bigo_pt_client_enable(struct bigo_core *core);
 void bigo_pt_client_disable(struct bigo_core *core);
 void bigo_get_cache_info(struct bigo_core *core, struct bigo_cache_info *cinfo);
 void bigo_bypass_ssmt_pid(struct bigo_core *core);
 #else
-static inline void bigo_pt_client_register(struct device_node *node, struct bigo_core *core) { }
+static inline int bigo_pt_client_register(struct device_node *node, struct bigo_core *core) { }
 static inline void bigo_pt_client_unregister(struct bigo_core *core) { }
 static inline int bigo_pt_client_enable(struct bigo_core *core) { return -EINVAL; }
 static inline void bigo_pt_client_disable(struct bigo_core *core) { }