diff --git a/drivers/video/rockchip/rga3/include/rga_drv.h b/drivers/video/rockchip/rga3/include/rga_drv.h index 37e6bd8bb9b3..9c5688107542 100644 --- a/drivers/video/rockchip/rga3/include/rga_drv.h +++ b/drivers/video/rockchip/rga3/include/rga_drv.h @@ -88,7 +88,7 @@ #define DRIVER_MAJOR_VERISON 1 #define DRIVER_MINOR_VERSION 3 -#define DRIVER_REVISION_VERSION 5 +#define DRIVER_REVISION_VERSION 6 #define DRIVER_PATCH_VERSION #define DRIVER_VERSION (STR(DRIVER_MAJOR_VERISON) "." STR(DRIVER_MINOR_VERSION) \ @@ -450,6 +450,9 @@ struct rga_drvdata_t { #ifdef CONFIG_ROCKCHIP_RGA_DEBUGGER struct rga_debugger *debugger; #endif + + bool shutdown; + struct rw_semaphore rwsem; }; struct rga_irqs_data_t { diff --git a/drivers/video/rockchip/rga3/include/rga_job.h b/drivers/video/rockchip/rga3/include/rga_job.h index 2bdf1a9258d0..bdd9ab9ed74a 100644 --- a/drivers/video/rockchip/rga3/include/rga_job.h +++ b/drivers/video/rockchip/rga3/include/rga_job.h @@ -38,6 +38,7 @@ struct rga_request *rga_request_lookup(struct rga_pending_request_manager *reque uint32_t id); int rga_request_commit(struct rga_request *user_request); +void rga_request_scheduler_shutdown(struct rga_scheduler_t *scheduler); void rga_request_scheduler_abort(struct rga_scheduler_t *scheduler); void rga_request_session_destroy_abort(struct rga_session *session); int rga_request_put(struct rga_request *request); diff --git a/drivers/video/rockchip/rga3/rga_drv.c b/drivers/video/rockchip/rga3/rga_drv.c index fc162f3abca1..37d27b7d6dc9 100644 --- a/drivers/video/rockchip/rga3/rga_drv.c +++ b/drivers/video/rockchip/rga3/rga_drv.c @@ -927,6 +927,15 @@ static long rga_ioctl(struct file *file, uint32_t cmd, unsigned long arg) if (DEBUGGER_EN(NONUSE)) return 0; + down_read(&rga_drvdata->rwsem); + + if (rga_drvdata->shutdown) { + rga_log("driver has been shutdown\n"); + up_read(&rga_drvdata->rwsem); + + return -EBUSY; + } + switch (cmd) { case RGA_BLIT_SYNC: case RGA_BLIT_ASYNC: @@ -1053,6 +1062,8 @@ static long rga_ioctl(struct file *file, uint32_t cmd, unsigned long arg) break; } + up_read(&rga_drvdata->rwsem); + return ret; } @@ -1442,17 +1453,38 @@ pm_disable: static int rga_drv_remove(struct platform_device *pdev) { + struct rga_scheduler_t *scheduler = NULL; + + down_write(&rga_drvdata->rwsem); + rga_drvdata->shutdown = true; + + scheduler = (struct rga_scheduler_t *)platform_get_drvdata(pdev); + if (scheduler) + rga_request_scheduler_shutdown(scheduler); + #ifndef RGA_DISABLE_PM device_init_wakeup(&pdev->dev, false); pm_runtime_disable(&pdev->dev); #endif /* #ifndef RGA_DISABLE_PM */ + up_write(&rga_drvdata->rwsem); + return 0; } +static void rga_drv_shutdown(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + + rga_drv_remove(pdev); + + dev_info(dev, "shutdown success\n"); +} + static struct platform_driver rga3_driver = { .probe = rga_drv_probe, .remove = rga_drv_remove, + .shutdown = rga_drv_shutdown, .driver = { .name = "rga3", .of_match_table = of_match_ptr(rga3_dt_ids), @@ -1462,6 +1494,7 @@ static struct platform_driver rga3_driver = { static struct platform_driver rga2_driver = { .probe = rga_drv_probe, .remove = rga_drv_remove, + .shutdown = rga_drv_shutdown, .driver = { .name = "rga2", .of_match_table = of_match_ptr(rga2_dt_ids), @@ -1479,6 +1512,8 @@ static int __init rga_init(void) } mutex_init(&rga_drvdata->lock); + init_rwsem(&rga_drvdata->rwsem); + rga_drvdata->shutdown = false; ret = platform_driver_register(&rga3_driver); if (ret != 0) { diff --git a/drivers/video/rockchip/rga3/rga_job.c b/drivers/video/rockchip/rga3/rga_job.c index 25e1b978f2e9..dd184821b373 100644 --- a/drivers/video/rockchip/rga3/rga_job.c +++ b/drivers/video/rockchip/rga3/rga_job.c @@ -633,6 +633,56 @@ struct rga_request *rga_request_lookup(struct rga_pending_request_manager *manag return request; } +void rga_request_scheduler_shutdown(struct rga_scheduler_t *scheduler) +{ + struct rga_job *job, *job_q; + unsigned long flags; + + rga_power_enable(scheduler); + + spin_lock_irqsave(&scheduler->irq_lock, flags); + + job = scheduler->running_job; + if (job) { + if (test_bit(RGA_JOB_STATE_INTR_ERR, &job->state) || + test_bit(RGA_JOB_STATE_FINISH, &job->state)) { + spin_unlock_irqrestore(&scheduler->irq_lock, flags); + goto finish; + } + + scheduler->running_job = NULL; + scheduler->status = RGA_SCHEDULER_ABORT; + scheduler->ops->soft_reset(scheduler); + + spin_unlock_irqrestore(&scheduler->irq_lock, flags); + + rga_mm_unmap_job_info(job); + + job->ret = -EBUSY; + rga_request_release_signal(scheduler, job); + + /* + * Since the running job was abort, turn off the power here that + * should have been turned off after job done (corresponds to + * power_enable in rga_job_run()). + */ + rga_power_disable(scheduler); + } else { + /* Clean up the jobs in the todo list that need to be free. */ + list_for_each_entry_safe(job, job_q, &scheduler->todo_list, head) { + rga_mm_unmap_job_info(job); + + job->ret = -EBUSY; + rga_request_release_signal(scheduler, job); + } + + spin_unlock_irqrestore(&scheduler->irq_lock, flags); + } + +finish: + rga_power_disable(scheduler); +} + void rga_request_scheduler_abort(struct rga_scheduler_t *scheduler) { struct rga_job *job;