Loading drivers/gpu/msm/kgsl_pwrctrl.c +5 −3 Original line number Diff line number Diff line Loading @@ -172,7 +172,8 @@ static unsigned int _adjust_pwrlevel(struct kgsl_pwrctrl *pwr, int level, { unsigned int max_pwrlevel = max_t(unsigned int, pwr->thermal_pwrlevel, pwr->max_pwrlevel); unsigned int min_pwrlevel = max_t(unsigned int, pwr->thermal_pwrlevel, unsigned int min_pwrlevel = min_t(unsigned int, pwr->thermal_pwrlevel_floor, pwr->min_pwrlevel); switch (pwrc->type) { Loading Loading @@ -2263,6 +2264,7 @@ int kgsl_pwrctrl_init(struct kgsl_device *device) pwr->max_pwrlevel = 0; pwr->min_pwrlevel = pwr->num_pwrlevels - 2; pwr->thermal_pwrlevel = 0; pwr->thermal_pwrlevel_floor = pwr->min_pwrlevel; pwr->wakeup_maxpwrlevel = 0; Loading drivers/gpu/msm/kgsl_pwrctrl.h +5 −1 Original line number Diff line number Diff line /* Copyright (c) 2010-2017, The Linux Foundation. All rights reserved. /* Copyright (c) 2010-2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and Loading Loading @@ -125,9 +125,11 @@ struct kgsl_regulator { * @grp_clks - Array of clocks structures that we control * @power_flags - Control flags for power * @pwrlevels - List of supported power levels * @nb - Notifier block to receive GPU OPP change event * @active_pwrlevel - The currently active power level * @previous_pwrlevel - The power level before transition * @thermal_pwrlevel - maximum powerlevel constraint from thermal * @thermal_pwrlevel_floor - minimum powerlevel constraint from thermal * @default_pwrlevel - device wake up power level * @max_pwrlevel - maximum allowable powerlevel per the user * @min_pwrlevel - minimum allowable powerlevel per the user Loading Loading @@ -179,9 +181,11 @@ struct kgsl_pwrctrl { unsigned long power_flags; unsigned long ctrl_flags; struct kgsl_pwrlevel pwrlevels[KGSL_MAX_PWRLEVELS]; struct notifier_block nb; unsigned int active_pwrlevel; unsigned int previous_pwrlevel; unsigned int thermal_pwrlevel; unsigned int thermal_pwrlevel_floor; unsigned int default_pwrlevel; unsigned int wakeup_maxpwrlevel; unsigned int max_pwrlevel; Loading drivers/gpu/msm/kgsl_pwrscale.c +128 −15 Original line number Diff line number Diff line /* Copyright (c) 2010-2017, The Linux Foundation. All rights reserved. /* Copyright (c) 2010-2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and Loading @@ -15,6 +15,7 @@ #include <linux/kernel.h> #include <linux/hrtimer.h> #include <linux/devfreq_cooling.h> #include <linux/pm_opp.h> #include "kgsl.h" #include "kgsl_pwrscale.h" Loading Loading @@ -533,7 +534,6 @@ int kgsl_devfreq_target(struct device *dev, unsigned long *freq, u32 flags) int level; unsigned int i; unsigned long cur_freq, rec_freq; struct dev_pm_opp *opp; if (device == NULL) return -ENODEV; Loading @@ -552,20 +552,7 @@ int kgsl_devfreq_target(struct device *dev, unsigned long *freq, u32 flags) return 0; } /* * Thermal framework might have disabled/enabled OPP entries * for mitigation. So find the recommended frequency matching * the available opp entries */ rcu_read_lock(); rec_freq = *freq; opp = devfreq_recommended_opp(dev, &rec_freq, flags); if (IS_ERR(opp)) { rcu_read_unlock(); return PTR_ERR(opp); } rec_freq = dev_pm_opp_get_freq(opp); rcu_read_unlock(); mutex_lock(&device->mutex); cur_freq = kgsl_pwrctrl_active_freq(pwr); Loading Loading @@ -870,6 +857,125 @@ int kgsl_busmon_get_cur_freq(struct device *dev, unsigned long *freq) return 0; } /* * opp_notify - Callback function registered to receive OPP events. * @nb: The notifier block * @type: The event type. Two OPP events are expected in this function: * - OPP_EVENT_ENABLE: an GPU OPP is enabled. The in_opp parameter * contains the OPP that is enabled * - OPP_EVENT_DISALBE: an GPU OPP is disabled. The in_opp parameter * contains the OPP that is disabled. * @in_opp: the GPU OPP whose status is changed and triggered the event * * GPU OPP event callback function. The function subscribe GPU OPP status * change and update thermal power level accordingly. */ static int opp_notify(struct notifier_block *nb, unsigned long type, void *in_opp) { int result = -EINVAL, level, min_level, max_level; struct kgsl_pwrctrl *pwr = container_of(nb, struct kgsl_pwrctrl, nb); struct kgsl_device *device = container_of(pwr, struct kgsl_device, pwrctrl); struct device *dev = &device->pdev->dev; struct dev_pm_opp *opp; unsigned long min_freq = 0, max_freq = pwr->pwrlevels[0].gpu_freq; if (type != OPP_EVENT_ENABLE && type != OPP_EVENT_DISABLE) return result; rcu_read_lock(); opp = dev_pm_opp_find_freq_floor(dev, &max_freq); if (IS_ERR(opp)) { rcu_read_unlock(); return PTR_ERR(opp); } max_freq = dev_pm_opp_get_freq(opp); if (!max_freq) { rcu_read_unlock(); return result; } opp = dev_pm_opp_find_freq_ceil(dev, &min_freq); if (IS_ERR(opp)) min_freq = pwr->pwrlevels[pwr->min_pwrlevel].gpu_freq; rcu_read_unlock(); mutex_lock(&device->mutex); max_level = pwr->thermal_pwrlevel; min_level = pwr->thermal_pwrlevel_floor; /* Thermal limit cannot be lower than lowest non-zero operating freq */ for (level = 0; level < (pwr->num_pwrlevels - 1); level++) if (pwr->pwrlevels[level].gpu_freq == max_freq) max_level = level; if (pwr->pwrlevels[level].gpu_freq == min_freq) min_level = level; pwr->thermal_pwrlevel = max_level; pwr->thermal_pwrlevel_floor = min_level; /* Update the current level using the new limit */ kgsl_pwrctrl_pwrlevel_change(device, pwr->active_pwrlevel); mutex_unlock(&device->mutex); return 0; } /* * kgsl_opp_add_notifier - add a fine grained notifier. * @dev: The device * @nb: Notifier block that will receive updates. * * Add a notifier to receive GPU OPP_EVENT_* events * from the OPP framework. */ static int kgsl_opp_add_notifier(struct device *dev, struct notifier_block *nb) { struct srcu_notifier_head *nh; int ret = 0; rcu_read_lock(); nh = dev_pm_opp_get_notifier(dev); if (IS_ERR(nh)) ret = PTR_ERR(nh); rcu_read_unlock(); if (!ret) ret = srcu_notifier_chain_register(nh, nb); return ret; } /* * kgsl_opp_remove_notifier - remove registered opp event notifier. * @dev: The device * @nb: Notifier block that will receive updates. * * Remove gpu notifier that receives GPU OPP_EVENT_* events * from the OPP framework. */ static int kgsl_opp_remove_notifier(struct device *dev, struct notifier_block *nb) { struct srcu_notifier_head *nh; int ret = 0; rcu_read_lock(); nh = dev_pm_opp_get_notifier(dev); if (IS_ERR(nh)) ret = PTR_ERR(nh); rcu_read_unlock(); if (!ret) ret = srcu_notifier_chain_unregister(nh, nb); return ret; } /* * kgsl_pwrscale_init - Initialize pwrscale. Loading Loading @@ -900,6 +1006,10 @@ int kgsl_pwrscale_init(struct device *dev, const char *governor) gpu_profile = &pwrscale->gpu_profile; profile = &pwrscale->gpu_profile.profile; pwr->nb.notifier_call = opp_notify; kgsl_opp_add_notifier(dev, &pwr->nb); srcu_init_notifier_head(&pwrscale->nh); profile->initial_freq = Loading Loading @@ -1053,7 +1163,9 @@ void kgsl_pwrscale_close(struct kgsl_device *device) { int i; struct kgsl_pwrscale *pwrscale; struct kgsl_pwrctrl *pwr; pwr = &device->pwrctrl; pwrscale = &device->pwrscale; if (!pwrscale->devfreqptr) return; Loading @@ -1068,6 +1180,7 @@ void kgsl_pwrscale_close(struct kgsl_device *device) kgsl_midframe = NULL; device->pwrscale.devfreqptr = NULL; srcu_cleanup_notifier_head(&device->pwrscale.nh); kgsl_opp_remove_notifier(&device->pdev->dev, &pwr->nb); for (i = 0; i < KGSL_PWREVENT_MAX; i++) kfree(pwrscale->history[i].events); } Loading Loading
drivers/gpu/msm/kgsl_pwrctrl.c +5 −3 Original line number Diff line number Diff line Loading @@ -172,7 +172,8 @@ static unsigned int _adjust_pwrlevel(struct kgsl_pwrctrl *pwr, int level, { unsigned int max_pwrlevel = max_t(unsigned int, pwr->thermal_pwrlevel, pwr->max_pwrlevel); unsigned int min_pwrlevel = max_t(unsigned int, pwr->thermal_pwrlevel, unsigned int min_pwrlevel = min_t(unsigned int, pwr->thermal_pwrlevel_floor, pwr->min_pwrlevel); switch (pwrc->type) { Loading Loading @@ -2263,6 +2264,7 @@ int kgsl_pwrctrl_init(struct kgsl_device *device) pwr->max_pwrlevel = 0; pwr->min_pwrlevel = pwr->num_pwrlevels - 2; pwr->thermal_pwrlevel = 0; pwr->thermal_pwrlevel_floor = pwr->min_pwrlevel; pwr->wakeup_maxpwrlevel = 0; Loading
drivers/gpu/msm/kgsl_pwrctrl.h +5 −1 Original line number Diff line number Diff line /* Copyright (c) 2010-2017, The Linux Foundation. All rights reserved. /* Copyright (c) 2010-2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and Loading Loading @@ -125,9 +125,11 @@ struct kgsl_regulator { * @grp_clks - Array of clocks structures that we control * @power_flags - Control flags for power * @pwrlevels - List of supported power levels * @nb - Notifier block to receive GPU OPP change event * @active_pwrlevel - The currently active power level * @previous_pwrlevel - The power level before transition * @thermal_pwrlevel - maximum powerlevel constraint from thermal * @thermal_pwrlevel_floor - minimum powerlevel constraint from thermal * @default_pwrlevel - device wake up power level * @max_pwrlevel - maximum allowable powerlevel per the user * @min_pwrlevel - minimum allowable powerlevel per the user Loading Loading @@ -179,9 +181,11 @@ struct kgsl_pwrctrl { unsigned long power_flags; unsigned long ctrl_flags; struct kgsl_pwrlevel pwrlevels[KGSL_MAX_PWRLEVELS]; struct notifier_block nb; unsigned int active_pwrlevel; unsigned int previous_pwrlevel; unsigned int thermal_pwrlevel; unsigned int thermal_pwrlevel_floor; unsigned int default_pwrlevel; unsigned int wakeup_maxpwrlevel; unsigned int max_pwrlevel; Loading
drivers/gpu/msm/kgsl_pwrscale.c +128 −15 Original line number Diff line number Diff line /* Copyright (c) 2010-2017, The Linux Foundation. All rights reserved. /* Copyright (c) 2010-2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and Loading @@ -15,6 +15,7 @@ #include <linux/kernel.h> #include <linux/hrtimer.h> #include <linux/devfreq_cooling.h> #include <linux/pm_opp.h> #include "kgsl.h" #include "kgsl_pwrscale.h" Loading Loading @@ -533,7 +534,6 @@ int kgsl_devfreq_target(struct device *dev, unsigned long *freq, u32 flags) int level; unsigned int i; unsigned long cur_freq, rec_freq; struct dev_pm_opp *opp; if (device == NULL) return -ENODEV; Loading @@ -552,20 +552,7 @@ int kgsl_devfreq_target(struct device *dev, unsigned long *freq, u32 flags) return 0; } /* * Thermal framework might have disabled/enabled OPP entries * for mitigation. So find the recommended frequency matching * the available opp entries */ rcu_read_lock(); rec_freq = *freq; opp = devfreq_recommended_opp(dev, &rec_freq, flags); if (IS_ERR(opp)) { rcu_read_unlock(); return PTR_ERR(opp); } rec_freq = dev_pm_opp_get_freq(opp); rcu_read_unlock(); mutex_lock(&device->mutex); cur_freq = kgsl_pwrctrl_active_freq(pwr); Loading Loading @@ -870,6 +857,125 @@ int kgsl_busmon_get_cur_freq(struct device *dev, unsigned long *freq) return 0; } /* * opp_notify - Callback function registered to receive OPP events. * @nb: The notifier block * @type: The event type. Two OPP events are expected in this function: * - OPP_EVENT_ENABLE: an GPU OPP is enabled. The in_opp parameter * contains the OPP that is enabled * - OPP_EVENT_DISALBE: an GPU OPP is disabled. The in_opp parameter * contains the OPP that is disabled. * @in_opp: the GPU OPP whose status is changed and triggered the event * * GPU OPP event callback function. The function subscribe GPU OPP status * change and update thermal power level accordingly. */ static int opp_notify(struct notifier_block *nb, unsigned long type, void *in_opp) { int result = -EINVAL, level, min_level, max_level; struct kgsl_pwrctrl *pwr = container_of(nb, struct kgsl_pwrctrl, nb); struct kgsl_device *device = container_of(pwr, struct kgsl_device, pwrctrl); struct device *dev = &device->pdev->dev; struct dev_pm_opp *opp; unsigned long min_freq = 0, max_freq = pwr->pwrlevels[0].gpu_freq; if (type != OPP_EVENT_ENABLE && type != OPP_EVENT_DISABLE) return result; rcu_read_lock(); opp = dev_pm_opp_find_freq_floor(dev, &max_freq); if (IS_ERR(opp)) { rcu_read_unlock(); return PTR_ERR(opp); } max_freq = dev_pm_opp_get_freq(opp); if (!max_freq) { rcu_read_unlock(); return result; } opp = dev_pm_opp_find_freq_ceil(dev, &min_freq); if (IS_ERR(opp)) min_freq = pwr->pwrlevels[pwr->min_pwrlevel].gpu_freq; rcu_read_unlock(); mutex_lock(&device->mutex); max_level = pwr->thermal_pwrlevel; min_level = pwr->thermal_pwrlevel_floor; /* Thermal limit cannot be lower than lowest non-zero operating freq */ for (level = 0; level < (pwr->num_pwrlevels - 1); level++) if (pwr->pwrlevels[level].gpu_freq == max_freq) max_level = level; if (pwr->pwrlevels[level].gpu_freq == min_freq) min_level = level; pwr->thermal_pwrlevel = max_level; pwr->thermal_pwrlevel_floor = min_level; /* Update the current level using the new limit */ kgsl_pwrctrl_pwrlevel_change(device, pwr->active_pwrlevel); mutex_unlock(&device->mutex); return 0; } /* * kgsl_opp_add_notifier - add a fine grained notifier. * @dev: The device * @nb: Notifier block that will receive updates. * * Add a notifier to receive GPU OPP_EVENT_* events * from the OPP framework. */ static int kgsl_opp_add_notifier(struct device *dev, struct notifier_block *nb) { struct srcu_notifier_head *nh; int ret = 0; rcu_read_lock(); nh = dev_pm_opp_get_notifier(dev); if (IS_ERR(nh)) ret = PTR_ERR(nh); rcu_read_unlock(); if (!ret) ret = srcu_notifier_chain_register(nh, nb); return ret; } /* * kgsl_opp_remove_notifier - remove registered opp event notifier. * @dev: The device * @nb: Notifier block that will receive updates. * * Remove gpu notifier that receives GPU OPP_EVENT_* events * from the OPP framework. */ static int kgsl_opp_remove_notifier(struct device *dev, struct notifier_block *nb) { struct srcu_notifier_head *nh; int ret = 0; rcu_read_lock(); nh = dev_pm_opp_get_notifier(dev); if (IS_ERR(nh)) ret = PTR_ERR(nh); rcu_read_unlock(); if (!ret) ret = srcu_notifier_chain_unregister(nh, nb); return ret; } /* * kgsl_pwrscale_init - Initialize pwrscale. Loading Loading @@ -900,6 +1006,10 @@ int kgsl_pwrscale_init(struct device *dev, const char *governor) gpu_profile = &pwrscale->gpu_profile; profile = &pwrscale->gpu_profile.profile; pwr->nb.notifier_call = opp_notify; kgsl_opp_add_notifier(dev, &pwr->nb); srcu_init_notifier_head(&pwrscale->nh); profile->initial_freq = Loading Loading @@ -1053,7 +1163,9 @@ void kgsl_pwrscale_close(struct kgsl_device *device) { int i; struct kgsl_pwrscale *pwrscale; struct kgsl_pwrctrl *pwr; pwr = &device->pwrctrl; pwrscale = &device->pwrscale; if (!pwrscale->devfreqptr) return; Loading @@ -1068,6 +1180,7 @@ void kgsl_pwrscale_close(struct kgsl_device *device) kgsl_midframe = NULL; device->pwrscale.devfreqptr = NULL; srcu_cleanup_notifier_head(&device->pwrscale.nh); kgsl_opp_remove_notifier(&device->pdev->dev, &pwr->nb); for (i = 0; i < KGSL_PWREVENT_MAX; i++) kfree(pwrscale->history[i].events); } Loading