diff --git a/Documentation/devicetree/bindings/gpu/adreno.txt b/Documentation/devicetree/bindings/gpu/adreno.txt index f5ae85d27692..453223dc195a 100644 --- a/Documentation/devicetree/bindings/gpu/adreno.txt +++ b/Documentation/devicetree/bindings/gpu/adreno.txt @@ -143,6 +143,12 @@ Optional Properties: Specify the name of GPU temperature sensor. This name will be used to get the temperature from the thermal driver API. +- qcom,enable-midframe-timer: + Boolean. Enables the use of midframe sampling timer. This timer + samples the GPU powerstats if the cmdbatch expiry takes longer than + the threshold set by KGSL_GOVERNOR_CALL_INTERVAL. Enable only if + target has NAP state enabled. + GPU Quirks: - qcom,gpu-quirk-two-pass-use-wfi: Signal the GPU to set Set TWOPASSUSEWFI bit in diff --git a/drivers/gpu/msm/adreno_dispatch.c b/drivers/gpu/msm/adreno_dispatch.c index 18c05e930216..0d068e9c5805 100644 --- a/drivers/gpu/msm/adreno_dispatch.c +++ b/drivers/gpu/msm/adreno_dispatch.c @@ -604,6 +604,16 @@ static int sendcmd(struct adreno_device *adreno_dev, if (!test_and_set_bit(ADRENO_DISPATCHER_ACTIVE, &dispatcher->priv)) reinit_completion(&dispatcher->idle_gate); + + /* + * We update power stats generally at the expire of + * cmdbatch. In cases where the cmdbatch takes a long + * time to finish, it will delay power stats update, + * in effect it will delay DCVS decision. Start a + * timer to update power state on expire of this timer. + */ + kgsl_pwrscale_midframe_timer_restart(device); + } else { kgsl_active_count_put(device); clear_bit(ADRENO_DISPATCHER_POWER, &dispatcher->priv); diff --git a/drivers/gpu/msm/kgsl_pwrctrl.c b/drivers/gpu/msm/kgsl_pwrctrl.c index 172de7406c26..cd9a82a9bf4a 100644 --- a/drivers/gpu/msm/kgsl_pwrctrl.c +++ b/drivers/gpu/msm/kgsl_pwrctrl.c @@ -2355,6 +2355,7 @@ static int _init(struct kgsl_device *device) case KGSL_STATE_ACTIVE: kgsl_pwrctrl_irq(device, KGSL_PWRFLAGS_OFF); del_timer_sync(&device->idle_timer); + kgsl_pwrscale_midframe_timer_cancel(device); device->ftbl->stop(device); /* fall through */ case KGSL_STATE_AWARE: @@ -2462,6 +2463,7 @@ _aware(struct kgsl_device *device) case KGSL_STATE_ACTIVE: kgsl_pwrctrl_irq(device, KGSL_PWRFLAGS_OFF); del_timer_sync(&device->idle_timer); + kgsl_pwrscale_midframe_timer_cancel(device); break; case KGSL_STATE_SLUMBER: status = kgsl_pwrctrl_enable(device); @@ -2486,6 +2488,8 @@ _nap(struct kgsl_device *device) return -EBUSY; } + kgsl_pwrscale_midframe_timer_cancel(device); + /* * Read HW busy counters before going to NAP state. * The data might be used by power scale governors @@ -2522,6 +2526,7 @@ _slumber(struct kgsl_device *device) /* fall through */ case KGSL_STATE_NAP: del_timer_sync(&device->idle_timer); + kgsl_pwrscale_midframe_timer_cancel(device); if (device->pwrctrl.thermal_cycle == CYCLE_ACTIVE) { device->pwrctrl.thermal_cycle = CYCLE_ENABLE; del_timer_sync(&device->pwrctrl.thermal_timer); diff --git a/drivers/gpu/msm/kgsl_pwrscale.c b/drivers/gpu/msm/kgsl_pwrscale.c index 85cd29b5364e..413a3098b0ef 100644 --- a/drivers/gpu/msm/kgsl_pwrscale.c +++ b/drivers/gpu/msm/kgsl_pwrscale.c @@ -13,6 +13,7 @@ #include <linux/export.h> #include <linux/kernel.h> +#include <linux/hrtimer.h> #include "kgsl.h" #include "kgsl_pwrscale.h" @@ -37,6 +38,18 @@ static struct kgsl_popp popp_param[POPP_MAX] = { {0, 0}, }; +/** +* struct kgsl_midframe_info - midframe power stats sampling info +* @timer - midframe sampling timer +* @timer_check_ws - Updates powerstats on midframe expiry +* @device - pointer to kgsl_device +*/ +static struct kgsl_midframe_info { + struct hrtimer timer; + struct work_struct timer_check_ws; + struct kgsl_device *device; +} *kgsl_midframe = NULL; + static void do_devfreq_suspend(struct work_struct *work); static void do_devfreq_resume(struct work_struct *work); static void do_devfreq_notify(struct work_struct *work); @@ -183,9 +196,57 @@ void kgsl_pwrscale_update(struct kgsl_device *device) if (device->state != KGSL_STATE_SLUMBER) queue_work(device->pwrscale.devfreq_wq, &device->pwrscale.devfreq_notify_ws); + + kgsl_pwrscale_midframe_timer_restart(device); } EXPORT_SYMBOL(kgsl_pwrscale_update); +void kgsl_pwrscale_midframe_timer_restart(struct kgsl_device *device) +{ + if (kgsl_midframe) { + WARN_ON(!mutex_is_locked(&device->mutex)); + + /* If the timer is already running, stop it */ + if (hrtimer_active(&kgsl_midframe->timer)) + hrtimer_cancel( + &kgsl_midframe->timer); + + hrtimer_start(&kgsl_midframe->timer, + ns_to_ktime(KGSL_GOVERNOR_CALL_INTERVAL + * NSEC_PER_USEC), HRTIMER_MODE_REL); + } +} +EXPORT_SYMBOL(kgsl_pwrscale_midframe_timer_restart); + +void kgsl_pwrscale_midframe_timer_cancel(struct kgsl_device *device) +{ + if (kgsl_midframe) { + WARN_ON(!mutex_is_locked(&device->mutex)); + hrtimer_cancel(&kgsl_midframe->timer); + } +} +EXPORT_SYMBOL(kgsl_pwrscale_midframe_timer_cancel); + +static void kgsl_pwrscale_midframe_timer_check(struct work_struct *work) +{ + struct kgsl_device *device = kgsl_midframe->device; + + mutex_lock(&device->mutex); + if (device->state == KGSL_STATE_ACTIVE) + kgsl_pwrscale_update(device); + mutex_unlock(&device->mutex); +} + +static enum hrtimer_restart kgsl_pwrscale_midframe_timer(struct hrtimer *timer) +{ + struct kgsl_device *device = kgsl_midframe->device; + + queue_work(device->pwrscale.devfreq_wq, + &kgsl_midframe->timer_check_ws); + + return HRTIMER_NORESTART; +} + /* * kgsl_pwrscale_disable - temporarily disable the governor * @device: The device @@ -852,6 +913,17 @@ int kgsl_pwrscale_init(struct device *dev, const char *governor) data->bin.ctxt_aware_busy_penalty = 12000; } + if (of_property_read_bool(device->pdev->dev.of_node, + "qcom,enable-midframe-timer")) { + kgsl_midframe = kzalloc( + sizeof(struct kgsl_midframe_info), GFP_KERNEL); + hrtimer_init(&kgsl_midframe->timer, + CLOCK_MONOTONIC, HRTIMER_MODE_REL); + kgsl_midframe->timer.function = + kgsl_pwrscale_midframe_timer; + kgsl_midframe->device = device; + } + /* * If there is a separate GX power rail, allow * independent modification to its voltage through @@ -900,6 +972,9 @@ int kgsl_pwrscale_init(struct device *dev, const char *governor) INIT_WORK(&pwrscale->devfreq_suspend_ws, do_devfreq_suspend); INIT_WORK(&pwrscale->devfreq_resume_ws, do_devfreq_resume); INIT_WORK(&pwrscale->devfreq_notify_ws, do_devfreq_notify); + if (kgsl_midframe) + INIT_WORK(&kgsl_midframe->timer_check_ws, + kgsl_pwrscale_midframe_timer_check); pwrscale->next_governor_call = ktime_add_us(ktime_get(), KGSL_GOVERNOR_CALL_INTERVAL); @@ -940,9 +1015,13 @@ void kgsl_pwrscale_close(struct kgsl_device *device) pwrscale = &device->pwrscale; if (!pwrscale->devfreqptr) return; + + kgsl_pwrscale_midframe_timer_cancel(device); flush_workqueue(pwrscale->devfreq_wq); destroy_workqueue(pwrscale->devfreq_wq); devfreq_remove_device(device->pwrscale.devfreqptr); + kfree(kgsl_midframe); + kgsl_midframe = NULL; device->pwrscale.devfreqptr = NULL; srcu_cleanup_notifier_head(&device->pwrscale.nh); for (i = 0; i < KGSL_PWREVENT_MAX; i++) diff --git a/drivers/gpu/msm/kgsl_pwrscale.h b/drivers/gpu/msm/kgsl_pwrscale.h index 0756a4490f22..184bfd1a2692 100644 --- a/drivers/gpu/msm/kgsl_pwrscale.h +++ b/drivers/gpu/msm/kgsl_pwrscale.h @@ -122,6 +122,9 @@ void kgsl_pwrscale_busy(struct kgsl_device *device); void kgsl_pwrscale_sleep(struct kgsl_device *device); void kgsl_pwrscale_wake(struct kgsl_device *device); +void kgsl_pwrscale_midframe_timer_restart(struct kgsl_device *device); +void kgsl_pwrscale_midframe_timer_cancel(struct kgsl_device *device); + void kgsl_pwrscale_enable(struct kgsl_device *device); void kgsl_pwrscale_disable(struct kgsl_device *device, bool turbo);