Loading drivers/gpu/msm/kgsl_device.h +13 −0 Original line number Diff line number Diff line Loading @@ -978,4 +978,17 @@ void kgsl_snapshot_add_section(struct kgsl_device *device, u16 id, size_t (*func)(struct kgsl_device *, u8 *, size_t, void *), void *priv); /** * struct kgsl_pwr_limit - limit structure for each client * @node: Local list node for the limits list * @level: requested power level * @device: pointer to the device structure */ struct kgsl_pwr_limit { struct list_head node; unsigned int level; struct kgsl_device *device; }; #endif /* __KGSL_DEVICE_H */ drivers/gpu/msm/kgsl_pwrctrl.c +202 −13 Original line number Diff line number Diff line Loading @@ -223,6 +223,33 @@ void kgsl_pwrctrl_pwrlevel_change_settings(struct kgsl_device *device, } } /** * kgsl_pwrctrl_set_thermal_cycle() - set the thermal cycle if required * @pwr: Pointer to the kgsl_pwrctrl struct * @level: the level to transition to */ void kgsl_pwrctrl_set_thermal_cycle(struct kgsl_pwrctrl *pwr, unsigned int new_level) { if (new_level != pwr->thermal_pwrlevel) return; if (pwr->thermal_pwrlevel == pwr->sysfs_pwr_limit->level) { /* Thermal cycle for sysfs pwr limit, start cycling*/ if (pwr->thermal_cycle == CYCLE_ENABLE) { pwr->thermal_cycle = CYCLE_ACTIVE; mod_timer(&pwr->thermal_timer, jiffies + (TH_HZ - pwr->thermal_timeout)); pwr->thermal_highlow = 1; } } else { /* Non sysfs pwr limit, stop thermal cycle if active*/ if (pwr->thermal_cycle == CYCLE_ACTIVE) { pwr->thermal_cycle = CYCLE_ENABLE; del_timer_sync(&pwr->thermal_timer); } } } /** * kgsl_pwrctrl_pwrlevel_change() - Validate and change power levels * @device: Pointer to the kgsl_device struct Loading Loading @@ -262,13 +289,7 @@ void kgsl_pwrctrl_pwrlevel_change(struct kgsl_device *device, * If thermal cycling is required and the new level hits the * thermal limit, kick off the cycling. */ if ((pwr->thermal_cycle == CYCLE_ENABLE) && (new_level == pwr->thermal_pwrlevel)) { pwr->thermal_cycle = CYCLE_ACTIVE; mod_timer(&pwr->thermal_timer, jiffies + (TH_HZ - pwr->thermal_timeout)); pwr->thermal_highlow = 1; } kgsl_pwrctrl_set_thermal_cycle(pwr, new_level); if (new_level == old_level) return; Loading Loading @@ -537,7 +558,8 @@ static ssize_t kgsl_pwrctrl_max_gpuclk_store(struct device *dev, unsigned int hfreq, diff, udiff, i; if ((val < pwr->pwrlevels[pwr->num_pwrlevels - 1].gpu_freq) || (val > pwr->pwrlevels[0].gpu_freq)) goto done; goto err; /* Find the neighboring frequencies */ for (i = 0; i < pwr->num_pwrlevels - 1; i++) { if ((pwr->pwrlevels[i].gpu_freq > val) && Loading @@ -546,6 +568,8 @@ static ssize_t kgsl_pwrctrl_max_gpuclk_store(struct device *dev, break; } } if (i == pwr->num_pwrlevels - 1) goto err; hfreq = pwr->pwrlevels[i].gpu_freq; diff = hfreq - pwr->pwrlevels[i + 1].gpu_freq; udiff = hfreq - val; Loading @@ -555,13 +579,14 @@ static ssize_t kgsl_pwrctrl_max_gpuclk_store(struct device *dev, pwr->thermal_cycle = CYCLE_DISABLE; del_timer_sync(&pwr->thermal_timer); } mutex_unlock(&device->mutex); pwr->thermal_pwrlevel = (unsigned int) level; /* Update the current level using the new limit */ kgsl_pwrctrl_pwrlevel_change(device, pwr->active_pwrlevel); if (pwr->sysfs_pwr_limit) kgsl_pwr_limits_set_freq(pwr->sysfs_pwr_limit, pwr->pwrlevels[level].gpu_freq); return count; done: err: mutex_unlock(&device->mutex); return count; } Loading Loading @@ -1447,6 +1472,10 @@ int kgsl_pwrctrl_init(struct kgsl_device *device) setup_timer(&pwr->thermal_timer, kgsl_thermal_timer, (unsigned long) device); INIT_LIST_HEAD(&pwr->limits); spin_lock_init(&pwr->limits_lock); pwr->sysfs_pwr_limit = kgsl_pwr_limits_add(KGSL_DEVICE_3D0); devfreq_vbif_register_callback(kgsl_get_bw); return result; Loading Loading @@ -1492,6 +1521,12 @@ void kgsl_pwrctrl_close(struct kgsl_device *device) pwr->grp_clks[0] = NULL; pwr->power_flags = 0; if (!IS_ERR_OR_NULL(pwr->sysfs_pwr_limit)) { list_del(&pwr->sysfs_pwr_limit->node); kfree(pwr->sysfs_pwr_limit); pwr->sysfs_pwr_limit = NULL; } } /** Loading Loading @@ -2015,3 +2050,157 @@ int kgsl_active_count_wait(struct kgsl_device *device, int count) return result; } EXPORT_SYMBOL(kgsl_active_count_wait); /** * _update_limits() - update the limits based on the current requests * @limit: Pointer to the limits structure * @reason: Reason for the update * @level: Level if any to be set * * Set the thermal pwrlevel based on the current limits */ static void _update_limits(struct kgsl_pwr_limit *limit, unsigned int reason, unsigned int level) { struct kgsl_device *device = limit->device; struct kgsl_pwrctrl *pwr = &device->pwrctrl; struct kgsl_pwr_limit *temp_limit; unsigned int max_level = 0; spin_lock(&pwr->limits_lock); switch (reason) { case KGSL_PWR_ADD_LIMIT: list_add(&limit->node, &pwr->limits); break; case KGSL_PWR_DEL_LIMIT: list_del(&limit->node); if (list_empty(&pwr->limits)) goto done; break; case KGSL_PWR_SET_LIMIT: limit->level = level; break; } list_for_each_entry(temp_limit, &pwr->limits, node) { max_level = max_t(unsigned int, max_level, temp_limit->level); } done: spin_unlock(&pwr->limits_lock); mutex_lock(&device->mutex); pwr->thermal_pwrlevel = max_level; kgsl_pwrctrl_pwrlevel_change(device, pwr->active_pwrlevel); mutex_unlock(&device->mutex); } /** * kgsl_pwr_limits_add() - Add a new pwr limit * @id: Device ID * * Allocate a pwr limit structure for the client, add it to the limits * list and return the pointer to the client */ void *kgsl_pwr_limits_add(enum kgsl_deviceid id) { struct kgsl_device *device = kgsl_get_device(id); struct kgsl_pwr_limit *limit; if (IS_ERR_OR_NULL(device)) return NULL; limit = kzalloc(sizeof(struct kgsl_pwr_limit), GFP_KERNEL); if (limit == NULL) return ERR_PTR(-ENOMEM); limit->device = device; _update_limits(limit, KGSL_PWR_ADD_LIMIT, 0); return limit; } EXPORT_SYMBOL(kgsl_pwr_limits_add); /** * kgsl_pwr_limits_del() - Unregister the pwr limit client and * adjust the thermal limits * @limit_ptr: Client handle * * Delete the client handle from the thermal list and adjust the * active clocks if needed. */ void kgsl_pwr_limits_del(void *limit_ptr) { struct kgsl_pwr_limit *limit = limit_ptr; if (IS_ERR(limit)) return; _update_limits(limit, KGSL_PWR_DEL_LIMIT, 0); kfree(limit); } EXPORT_SYMBOL(kgsl_pwr_limits_del); /** * kgsl_pwr_limits_set_freq() - Set the requested limit for the client * @limit_ptr: Client handle * @freq: Client requested frequency * * Set the new limit for the client and adjust the clocks */ int kgsl_pwr_limits_set_freq(void *limit_ptr, unsigned int freq) { struct kgsl_pwrctrl *pwr; struct kgsl_pwr_limit *limit = limit_ptr; int level; if (IS_ERR(limit)) return -EINVAL; pwr = &limit->device->pwrctrl; level = _get_nearest_pwrlevel(pwr, freq); if (level < 0) return -EINVAL; _update_limits(limit, KGSL_PWR_SET_LIMIT, level); return 0; } EXPORT_SYMBOL(kgsl_pwr_limits_set_freq); /** * kgsl_pwr_limits_set_default() - Set the default thermal limit for the client * @limit_ptr: Client handle * * Set the default for the client and adjust the clocks */ void kgsl_pwr_limits_set_default(void *limit_ptr) { struct kgsl_pwr_limit *limit = limit_ptr; if (IS_ERR(limit)) return; _update_limits(limit, KGSL_PWR_SET_LIMIT, 0); } EXPORT_SYMBOL(kgsl_pwr_limits_set_default); /** * kgsl_pwr_limits_get_freq() - Get the current limit * @id: Device ID * * Get the current limit set for the device */ unsigned int kgsl_pwr_limits_get_freq(enum kgsl_deviceid id) { struct kgsl_device *device = kgsl_get_device(id); struct kgsl_pwrctrl *pwr; unsigned int freq; if (IS_ERR_OR_NULL(device)) return 0; pwr = &device->pwrctrl; mutex_lock(&device->mutex); freq = pwr->pwrlevels[pwr->thermal_pwrlevel].gpu_freq; mutex_unlock(&device->mutex); return freq; } EXPORT_SYMBOL(kgsl_pwr_limits_get_freq); drivers/gpu/msm/kgsl_pwrctrl.h +15 −0 Original line number Diff line number Diff line Loading @@ -41,6 +41,10 @@ { KGSL_CONSTRAINT_PWR_MIN, "Min" }, \ { KGSL_CONSTRAINT_PWR_MAX, "Max" } #define KGSL_PWR_ADD_LIMIT 0 #define KGSL_PWR_DEL_LIMIT 1 #define KGSL_PWR_SET_LIMIT 2 /* * States for thermal cycling. _DISABLE means that no cycling has been * requested. _ENABLE means that cycling has been requested, but GPU Loading Loading @@ -104,6 +108,14 @@ struct kgsl_pwr_constraint { * @constraint - currently active power constraint * @superfast - Boolean flag to indicate that the GPU start should be run in the * higher priority thread * @thermal_cycle_ws - Work struct for scheduling thermal cycling * @thermal_timer - Timer for thermal cycling * @thermal_timeout - Cycling timeout for switching between frequencies * @thermal_cycle - Is thermal cycling enabled * @thermal_highlow - flag for swithcing between high and low frequency * @limits - list head for limits * @limits_lock - spin lock to protect limits list * @sysfs_pwr_limit - pointer to the sysfs limits node */ struct kgsl_pwrctrl { Loading Loading @@ -143,6 +155,9 @@ struct kgsl_pwrctrl { uint32_t thermal_timeout; uint32_t thermal_cycle; uint32_t thermal_highlow; struct list_head limits; spinlock_t limits_lock; struct kgsl_pwr_limit *sysfs_pwr_limit; }; void kgsl_pwrctrl_irq(struct kgsl_device *device, int state); Loading include/linux/msm_kgsl.h +7 −0 Original line number Diff line number Diff line Loading @@ -74,6 +74,13 @@ struct kgsl_device_platform_data { unsigned int pm_qos_wakeup_latency; }; /* Limits mitigations APIs */ void *kgsl_pwr_limits_add(enum kgsl_deviceid id); void kgsl_pwr_limits_del(void *limit); int kgsl_pwr_limits_set_freq(void *limit, unsigned int freq); void kgsl_pwr_limits_set_default(void *limit); unsigned int kgsl_pwr_limits_get_freq(enum kgsl_deviceid id); #ifdef CONFIG_MSM_KGSL_DRM int kgsl_gem_obj_addr(int drm_fd, int handle, unsigned long *start, unsigned long *len); Loading Loading
drivers/gpu/msm/kgsl_device.h +13 −0 Original line number Diff line number Diff line Loading @@ -978,4 +978,17 @@ void kgsl_snapshot_add_section(struct kgsl_device *device, u16 id, size_t (*func)(struct kgsl_device *, u8 *, size_t, void *), void *priv); /** * struct kgsl_pwr_limit - limit structure for each client * @node: Local list node for the limits list * @level: requested power level * @device: pointer to the device structure */ struct kgsl_pwr_limit { struct list_head node; unsigned int level; struct kgsl_device *device; }; #endif /* __KGSL_DEVICE_H */
drivers/gpu/msm/kgsl_pwrctrl.c +202 −13 Original line number Diff line number Diff line Loading @@ -223,6 +223,33 @@ void kgsl_pwrctrl_pwrlevel_change_settings(struct kgsl_device *device, } } /** * kgsl_pwrctrl_set_thermal_cycle() - set the thermal cycle if required * @pwr: Pointer to the kgsl_pwrctrl struct * @level: the level to transition to */ void kgsl_pwrctrl_set_thermal_cycle(struct kgsl_pwrctrl *pwr, unsigned int new_level) { if (new_level != pwr->thermal_pwrlevel) return; if (pwr->thermal_pwrlevel == pwr->sysfs_pwr_limit->level) { /* Thermal cycle for sysfs pwr limit, start cycling*/ if (pwr->thermal_cycle == CYCLE_ENABLE) { pwr->thermal_cycle = CYCLE_ACTIVE; mod_timer(&pwr->thermal_timer, jiffies + (TH_HZ - pwr->thermal_timeout)); pwr->thermal_highlow = 1; } } else { /* Non sysfs pwr limit, stop thermal cycle if active*/ if (pwr->thermal_cycle == CYCLE_ACTIVE) { pwr->thermal_cycle = CYCLE_ENABLE; del_timer_sync(&pwr->thermal_timer); } } } /** * kgsl_pwrctrl_pwrlevel_change() - Validate and change power levels * @device: Pointer to the kgsl_device struct Loading Loading @@ -262,13 +289,7 @@ void kgsl_pwrctrl_pwrlevel_change(struct kgsl_device *device, * If thermal cycling is required and the new level hits the * thermal limit, kick off the cycling. */ if ((pwr->thermal_cycle == CYCLE_ENABLE) && (new_level == pwr->thermal_pwrlevel)) { pwr->thermal_cycle = CYCLE_ACTIVE; mod_timer(&pwr->thermal_timer, jiffies + (TH_HZ - pwr->thermal_timeout)); pwr->thermal_highlow = 1; } kgsl_pwrctrl_set_thermal_cycle(pwr, new_level); if (new_level == old_level) return; Loading Loading @@ -537,7 +558,8 @@ static ssize_t kgsl_pwrctrl_max_gpuclk_store(struct device *dev, unsigned int hfreq, diff, udiff, i; if ((val < pwr->pwrlevels[pwr->num_pwrlevels - 1].gpu_freq) || (val > pwr->pwrlevels[0].gpu_freq)) goto done; goto err; /* Find the neighboring frequencies */ for (i = 0; i < pwr->num_pwrlevels - 1; i++) { if ((pwr->pwrlevels[i].gpu_freq > val) && Loading @@ -546,6 +568,8 @@ static ssize_t kgsl_pwrctrl_max_gpuclk_store(struct device *dev, break; } } if (i == pwr->num_pwrlevels - 1) goto err; hfreq = pwr->pwrlevels[i].gpu_freq; diff = hfreq - pwr->pwrlevels[i + 1].gpu_freq; udiff = hfreq - val; Loading @@ -555,13 +579,14 @@ static ssize_t kgsl_pwrctrl_max_gpuclk_store(struct device *dev, pwr->thermal_cycle = CYCLE_DISABLE; del_timer_sync(&pwr->thermal_timer); } mutex_unlock(&device->mutex); pwr->thermal_pwrlevel = (unsigned int) level; /* Update the current level using the new limit */ kgsl_pwrctrl_pwrlevel_change(device, pwr->active_pwrlevel); if (pwr->sysfs_pwr_limit) kgsl_pwr_limits_set_freq(pwr->sysfs_pwr_limit, pwr->pwrlevels[level].gpu_freq); return count; done: err: mutex_unlock(&device->mutex); return count; } Loading Loading @@ -1447,6 +1472,10 @@ int kgsl_pwrctrl_init(struct kgsl_device *device) setup_timer(&pwr->thermal_timer, kgsl_thermal_timer, (unsigned long) device); INIT_LIST_HEAD(&pwr->limits); spin_lock_init(&pwr->limits_lock); pwr->sysfs_pwr_limit = kgsl_pwr_limits_add(KGSL_DEVICE_3D0); devfreq_vbif_register_callback(kgsl_get_bw); return result; Loading Loading @@ -1492,6 +1521,12 @@ void kgsl_pwrctrl_close(struct kgsl_device *device) pwr->grp_clks[0] = NULL; pwr->power_flags = 0; if (!IS_ERR_OR_NULL(pwr->sysfs_pwr_limit)) { list_del(&pwr->sysfs_pwr_limit->node); kfree(pwr->sysfs_pwr_limit); pwr->sysfs_pwr_limit = NULL; } } /** Loading Loading @@ -2015,3 +2050,157 @@ int kgsl_active_count_wait(struct kgsl_device *device, int count) return result; } EXPORT_SYMBOL(kgsl_active_count_wait); /** * _update_limits() - update the limits based on the current requests * @limit: Pointer to the limits structure * @reason: Reason for the update * @level: Level if any to be set * * Set the thermal pwrlevel based on the current limits */ static void _update_limits(struct kgsl_pwr_limit *limit, unsigned int reason, unsigned int level) { struct kgsl_device *device = limit->device; struct kgsl_pwrctrl *pwr = &device->pwrctrl; struct kgsl_pwr_limit *temp_limit; unsigned int max_level = 0; spin_lock(&pwr->limits_lock); switch (reason) { case KGSL_PWR_ADD_LIMIT: list_add(&limit->node, &pwr->limits); break; case KGSL_PWR_DEL_LIMIT: list_del(&limit->node); if (list_empty(&pwr->limits)) goto done; break; case KGSL_PWR_SET_LIMIT: limit->level = level; break; } list_for_each_entry(temp_limit, &pwr->limits, node) { max_level = max_t(unsigned int, max_level, temp_limit->level); } done: spin_unlock(&pwr->limits_lock); mutex_lock(&device->mutex); pwr->thermal_pwrlevel = max_level; kgsl_pwrctrl_pwrlevel_change(device, pwr->active_pwrlevel); mutex_unlock(&device->mutex); } /** * kgsl_pwr_limits_add() - Add a new pwr limit * @id: Device ID * * Allocate a pwr limit structure for the client, add it to the limits * list and return the pointer to the client */ void *kgsl_pwr_limits_add(enum kgsl_deviceid id) { struct kgsl_device *device = kgsl_get_device(id); struct kgsl_pwr_limit *limit; if (IS_ERR_OR_NULL(device)) return NULL; limit = kzalloc(sizeof(struct kgsl_pwr_limit), GFP_KERNEL); if (limit == NULL) return ERR_PTR(-ENOMEM); limit->device = device; _update_limits(limit, KGSL_PWR_ADD_LIMIT, 0); return limit; } EXPORT_SYMBOL(kgsl_pwr_limits_add); /** * kgsl_pwr_limits_del() - Unregister the pwr limit client and * adjust the thermal limits * @limit_ptr: Client handle * * Delete the client handle from the thermal list and adjust the * active clocks if needed. */ void kgsl_pwr_limits_del(void *limit_ptr) { struct kgsl_pwr_limit *limit = limit_ptr; if (IS_ERR(limit)) return; _update_limits(limit, KGSL_PWR_DEL_LIMIT, 0); kfree(limit); } EXPORT_SYMBOL(kgsl_pwr_limits_del); /** * kgsl_pwr_limits_set_freq() - Set the requested limit for the client * @limit_ptr: Client handle * @freq: Client requested frequency * * Set the new limit for the client and adjust the clocks */ int kgsl_pwr_limits_set_freq(void *limit_ptr, unsigned int freq) { struct kgsl_pwrctrl *pwr; struct kgsl_pwr_limit *limit = limit_ptr; int level; if (IS_ERR(limit)) return -EINVAL; pwr = &limit->device->pwrctrl; level = _get_nearest_pwrlevel(pwr, freq); if (level < 0) return -EINVAL; _update_limits(limit, KGSL_PWR_SET_LIMIT, level); return 0; } EXPORT_SYMBOL(kgsl_pwr_limits_set_freq); /** * kgsl_pwr_limits_set_default() - Set the default thermal limit for the client * @limit_ptr: Client handle * * Set the default for the client and adjust the clocks */ void kgsl_pwr_limits_set_default(void *limit_ptr) { struct kgsl_pwr_limit *limit = limit_ptr; if (IS_ERR(limit)) return; _update_limits(limit, KGSL_PWR_SET_LIMIT, 0); } EXPORT_SYMBOL(kgsl_pwr_limits_set_default); /** * kgsl_pwr_limits_get_freq() - Get the current limit * @id: Device ID * * Get the current limit set for the device */ unsigned int kgsl_pwr_limits_get_freq(enum kgsl_deviceid id) { struct kgsl_device *device = kgsl_get_device(id); struct kgsl_pwrctrl *pwr; unsigned int freq; if (IS_ERR_OR_NULL(device)) return 0; pwr = &device->pwrctrl; mutex_lock(&device->mutex); freq = pwr->pwrlevels[pwr->thermal_pwrlevel].gpu_freq; mutex_unlock(&device->mutex); return freq; } EXPORT_SYMBOL(kgsl_pwr_limits_get_freq);
drivers/gpu/msm/kgsl_pwrctrl.h +15 −0 Original line number Diff line number Diff line Loading @@ -41,6 +41,10 @@ { KGSL_CONSTRAINT_PWR_MIN, "Min" }, \ { KGSL_CONSTRAINT_PWR_MAX, "Max" } #define KGSL_PWR_ADD_LIMIT 0 #define KGSL_PWR_DEL_LIMIT 1 #define KGSL_PWR_SET_LIMIT 2 /* * States for thermal cycling. _DISABLE means that no cycling has been * requested. _ENABLE means that cycling has been requested, but GPU Loading Loading @@ -104,6 +108,14 @@ struct kgsl_pwr_constraint { * @constraint - currently active power constraint * @superfast - Boolean flag to indicate that the GPU start should be run in the * higher priority thread * @thermal_cycle_ws - Work struct for scheduling thermal cycling * @thermal_timer - Timer for thermal cycling * @thermal_timeout - Cycling timeout for switching between frequencies * @thermal_cycle - Is thermal cycling enabled * @thermal_highlow - flag for swithcing between high and low frequency * @limits - list head for limits * @limits_lock - spin lock to protect limits list * @sysfs_pwr_limit - pointer to the sysfs limits node */ struct kgsl_pwrctrl { Loading Loading @@ -143,6 +155,9 @@ struct kgsl_pwrctrl { uint32_t thermal_timeout; uint32_t thermal_cycle; uint32_t thermal_highlow; struct list_head limits; spinlock_t limits_lock; struct kgsl_pwr_limit *sysfs_pwr_limit; }; void kgsl_pwrctrl_irq(struct kgsl_device *device, int state); Loading
include/linux/msm_kgsl.h +7 −0 Original line number Diff line number Diff line Loading @@ -74,6 +74,13 @@ struct kgsl_device_platform_data { unsigned int pm_qos_wakeup_latency; }; /* Limits mitigations APIs */ void *kgsl_pwr_limits_add(enum kgsl_deviceid id); void kgsl_pwr_limits_del(void *limit); int kgsl_pwr_limits_set_freq(void *limit, unsigned int freq); void kgsl_pwr_limits_set_default(void *limit); unsigned int kgsl_pwr_limits_get_freq(enum kgsl_deviceid id); #ifdef CONFIG_MSM_KGSL_DRM int kgsl_gem_obj_addr(int drm_fd, int handle, unsigned long *start, unsigned long *len); Loading