diff --git a/drivers/gpu/msm/adreno.c b/drivers/gpu/msm/adreno.c index 5039adfebda6..fcc21dbf8ced 100644 --- a/drivers/gpu/msm/adreno.c +++ b/drivers/gpu/msm/adreno.c @@ -1881,11 +1881,11 @@ static int adreno_last_close(struct kgsl_device *device) * Wait up to 1 second for the active count to go low * and then start complaining about it */ - if (kgsl_active_count_wait(device, 0)) { + if (kgsl_active_count_wait(device, 0, HZ)) { dev_err(device->dev, "Waiting for the active count to become 0\n"); - while (kgsl_active_count_wait(device, 0)) + while (kgsl_active_count_wait(device, 0, HZ)) dev_err(device->dev, "Still waiting for the active count\n"); } diff --git a/drivers/gpu/msm/adreno_a6xx_gmu.c b/drivers/gpu/msm/adreno_a6xx_gmu.c index 9fb8db6f99ca..b445687146a9 100644 --- a/drivers/gpu/msm/adreno_a6xx_gmu.c +++ b/drivers/gpu/msm/adreno_a6xx_gmu.c @@ -3201,7 +3201,7 @@ static int a6xx_gmu_pm_suspend(struct adreno_device *adreno_dev) reinit_completion(&device->halt_gate); /* wait for active count so device can be put in slumber */ - ret = kgsl_active_count_wait(device, 0); + ret = kgsl_active_count_wait(device, 0, HZ); if (ret) { dev_err(device->dev, "Timed out waiting for the active count\n"); @@ -3232,10 +3232,9 @@ static void a6xx_gmu_pm_resume(struct adreno_device *adreno_dev) struct kgsl_device *device = KGSL_DEVICE(adreno_dev); struct a6xx_gmu_device *gmu = to_a6xx_gmu(adreno_dev); - if (!test_bit(GMU_PRIV_PM_SUSPEND, &gmu->flags)) { - dev_err(device->dev, "resume invoked without a suspend\n"); + if (WARN(!test_bit(GMU_PRIV_PM_SUSPEND, &gmu->flags), + "resume invoked without a suspend\n")) return; - } adreno_dispatcher_unhalt(device); diff --git a/drivers/gpu/msm/adreno_a6xx_hwsched.c b/drivers/gpu/msm/adreno_a6xx_hwsched.c index 9d3983874a09..edd147109a04 100644 --- a/drivers/gpu/msm/adreno_a6xx_hwsched.c +++ b/drivers/gpu/msm/adreno_a6xx_hwsched.c @@ -672,12 +672,95 @@ static int a6xx_hwsched_bus_set(struct adreno_device *adreno_dev, int buslevel, return ret; } +static int a6xx_hwsched_pm_suspend(struct adreno_device *adreno_dev) +{ + struct kgsl_device *device = KGSL_DEVICE(adreno_dev); + struct a6xx_gmu_device *gmu = to_a6xx_gmu(adreno_dev); + int ret; + + if (test_bit(GMU_PRIV_PM_SUSPEND, &gmu->flags)) + return 0; + + trace_kgsl_pwr_request_state(device, KGSL_STATE_SUSPEND); + + /* Halt any new submissions */ + reinit_completion(&device->halt_gate); + + mutex_unlock(&device->mutex); + + /* Flush any currently running instances of the dispatcher */ + kthread_flush_worker(&kgsl_driver.worker); + + mutex_lock(&device->mutex); + + /* This ensures that dispatcher doesn't submit any new work */ + adreno_dispatcher_halt(device); + + /** + * Wait for the dispatcher to retire everything by waiting + * for the active count to go to zero. + */ + ret = kgsl_active_count_wait(device, 0, msecs_to_jiffies(100)); + if (ret) { + dev_err(device->dev, "Timed out waiting for the active count\n"); + goto err; + } + + if (test_bit(GMU_PRIV_GPU_STARTED, &gmu->flags)) { + unsigned long wait = jiffies + + msecs_to_jiffies(ADRENO_IDLE_TIMEOUT); + + do { + if (a6xx_hw_isidle(adreno_dev)) + break; + } while (time_before(jiffies, wait)); + + if (!a6xx_hw_isidle(adreno_dev)) { + dev_err(device->dev, "Timed out idling the gpu\n"); + ret = -ETIMEDOUT; + goto err; + } + + a6xx_hwsched_power_off(adreno_dev); + } + + set_bit(GMU_PRIV_PM_SUSPEND, &gmu->flags); + + trace_kgsl_pwr_set_state(device, KGSL_STATE_SUSPEND); + + return 0; + +err: + adreno_dispatcher_unhalt(device); + adreno_hwsched_start(adreno_dev); + + return ret; +} + +static void a6xx_hwsched_pm_resume(struct adreno_device *adreno_dev) +{ + struct kgsl_device *device = KGSL_DEVICE(adreno_dev); + struct a6xx_gmu_device *gmu = to_a6xx_gmu(adreno_dev); + + if (WARN(!test_bit(GMU_PRIV_PM_SUSPEND, &gmu->flags), + "resume invoked without a suspend\n")) + return; + + adreno_dispatcher_unhalt(device); + + adreno_hwsched_start(adreno_dev); + + clear_bit(GMU_PRIV_PM_SUSPEND, &gmu->flags); +} + const struct adreno_power_ops a6xx_hwsched_power_ops = { .first_open = a6xx_hwsched_first_open, .last_close = a6xx_hwsched_power_off, .active_count_get = a6xx_hwsched_active_count_get, .active_count_put = a6xx_hwsched_active_count_put, .touch_wakeup = a6xx_hwsched_touch_wakeup, + .pm_suspend = a6xx_hwsched_pm_suspend, + .pm_resume = a6xx_hwsched_pm_resume, .gpu_clock_set = a6xx_hwsched_clock_set, .gpu_bus_set = a6xx_hwsched_bus_set, }; diff --git a/drivers/gpu/msm/adreno_a6xx_rgmu.c b/drivers/gpu/msm/adreno_a6xx_rgmu.c index 0cb54da191c3..d0a921febe3c 100644 --- a/drivers/gpu/msm/adreno_a6xx_rgmu.c +++ b/drivers/gpu/msm/adreno_a6xx_rgmu.c @@ -1153,7 +1153,7 @@ static int a6xx_rgmu_pm_suspend(struct adreno_device *adreno_dev) reinit_completion(&device->halt_gate); /* wait for active count so device can be put in slumber */ - ret = kgsl_active_count_wait(device, 0); + ret = kgsl_active_count_wait(device, 0, HZ); if (ret) { dev_err(device->dev, "Timed out waiting for the active count\n"); @@ -1183,10 +1183,9 @@ static void a6xx_rgmu_pm_resume(struct adreno_device *adreno_dev) struct kgsl_device *device = KGSL_DEVICE(adreno_dev); struct a6xx_rgmu_device *rgmu = to_a6xx_rgmu(adreno_dev); - if (!test_bit(RGMU_PRIV_PM_SUSPEND, &rgmu->flags)) { - dev_err(device->dev, "resume invoked without a suspend\n"); + if (WARN(!test_bit(GMU_PRIV_PM_SUSPEND, &rgmu->flags), + "resume invoked without a suspend\n")) return; - } adreno_dispatcher_unhalt(device); diff --git a/drivers/gpu/msm/adreno_hwsched.c b/drivers/gpu/msm/adreno_hwsched.c index 1f015e4fbde4..9184a64d8d5c 100644 --- a/drivers/gpu/msm/adreno_hwsched.c +++ b/drivers/gpu/msm/adreno_hwsched.c @@ -296,6 +296,13 @@ static int hwsched_sendcmd(struct adreno_device *adreno_dev, mutex_lock(&device->mutex); + if (adreno_gpu_halt(adreno_dev) != 0) { + mutex_unlock(&device->mutex); + kmem_cache_free(obj_cache, obj); + return -EBUSY; + } + + if (kgsl_context_detached(context)) { mutex_unlock(&device->mutex); kmem_cache_free(obj_cache, obj); diff --git a/drivers/gpu/msm/kgsl_pwrctrl.c b/drivers/gpu/msm/kgsl_pwrctrl.c index 87245a77ee12..926c13c18100 100644 --- a/drivers/gpu/msm/kgsl_pwrctrl.c +++ b/drivers/gpu/msm/kgsl_pwrctrl.c @@ -2026,7 +2026,7 @@ static int _suspend(struct kgsl_device *device) /* drain to prevent from more commands being submitted */ device->ftbl->drain(device); /* wait for active count so device can be put in slumber */ - ret = kgsl_active_count_wait(device, 0); + ret = kgsl_active_count_wait(device, 0, HZ); if (ret) goto err; @@ -2155,17 +2155,10 @@ static int _check_active_count(struct kgsl_device *device, int count) return atomic_read(&device->active_cnt) > count ? 0 : 1; } -/** - * kgsl_active_count_wait() - Wait for activity to finish. - * @device: Pointer to a KGSL device - * @count: Active count value to wait for - * - * Block until the active_cnt value hits the desired value - */ -int kgsl_active_count_wait(struct kgsl_device *device, int count) +int kgsl_active_count_wait(struct kgsl_device *device, int count, + unsigned long wait_jiffies) { int result = 0; - long wait_jiffies = HZ; if (WARN_ON(!mutex_is_locked(&device->mutex))) return -EINVAL; diff --git a/drivers/gpu/msm/kgsl_pwrctrl.h b/drivers/gpu/msm/kgsl_pwrctrl.h index 6747b7d39555..eb2ce504455b 100644 --- a/drivers/gpu/msm/kgsl_pwrctrl.h +++ b/drivers/gpu/msm/kgsl_pwrctrl.h @@ -198,7 +198,16 @@ kgsl_pwrctrl_active_freq(struct kgsl_pwrctrl *pwr) return pwr->pwrlevels[pwr->active_pwrlevel].gpu_freq; } -int kgsl_active_count_wait(struct kgsl_device *device, int count); +/** + * kgsl_active_count_wait() - Wait for activity to finish. + * @device: Pointer to a KGSL device + * @count: Active count value to wait for + * @wait_jiffies: Jiffies to wait + * + * Block until the active_cnt value hits the desired value + */ +int kgsl_active_count_wait(struct kgsl_device *device, int count, + unsigned long wait_jiffies); void kgsl_pwrctrl_busy_time(struct kgsl_device *device, u64 time, u64 busy); void kgsl_pwrctrl_set_constraint(struct kgsl_device *device, struct kgsl_pwr_constraint *pwrc, uint32_t id);