diff --git a/drivers/gpu/msm/adreno_a6xx_gmu.c b/drivers/gpu/msm/adreno_a6xx_gmu.c index 8c7d4f1d05a2..acd6a97cb66c 100644 --- a/drivers/gpu/msm/adreno_a6xx_gmu.c +++ b/drivers/gpu/msm/adreno_a6xx_gmu.c @@ -2948,6 +2948,8 @@ static int a6xx_boot(struct adreno_device *adreno_dev) kgsl_pwrscale_wake(device); set_bit(GMU_PRIV_GPU_STARTED, &gmu->flags); + + device->pwrctrl.last_stat_updated = ktime_get(); device->state = KGSL_STATE_ACTIVE; trace_kgsl_pwr_set_state(device, KGSL_STATE_ACTIVE); @@ -3016,6 +3018,7 @@ static int a6xx_first_boot(struct adreno_device *adreno_dev) set_bit(GMU_PRIV_FIRST_BOOT_DONE, &gmu->flags); set_bit(GMU_PRIV_GPU_STARTED, &gmu->flags); + device->pwrctrl.last_stat_updated = ktime_get(); device->state = KGSL_STATE_ACTIVE; trace_kgsl_pwr_set_state(device, KGSL_STATE_ACTIVE); @@ -3277,6 +3280,8 @@ static void a6xx_gmu_touch_wakeup(struct adreno_device *adreno_dev) kgsl_pwrscale_wake(device); set_bit(GMU_PRIV_GPU_STARTED, &gmu->flags); + + device->pwrctrl.last_stat_updated = ktime_get(); device->state = KGSL_STATE_ACTIVE; trace_kgsl_pwr_set_state(device, KGSL_STATE_ACTIVE); diff --git a/drivers/gpu/msm/adreno_a6xx_hwsched.c b/drivers/gpu/msm/adreno_a6xx_hwsched.c index 433454a70619..4d8e7897e8cb 100644 --- a/drivers/gpu/msm/adreno_a6xx_hwsched.c +++ b/drivers/gpu/msm/adreno_a6xx_hwsched.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (c) 2020, The Linux Foundation. All rights reserved. + * Copyright (c) 2020-2021, The Linux Foundation. All rights reserved. */ #include @@ -500,6 +500,7 @@ static void a6xx_hwsched_touch_wakeup(struct adreno_device *adreno_dev) set_bit(GMU_PRIV_GPU_STARTED, &gmu->flags); + device->pwrctrl.last_stat_updated = ktime_get(); device->state = KGSL_STATE_ACTIVE; trace_kgsl_pwr_set_state(device, KGSL_STATE_ACTIVE); @@ -541,6 +542,8 @@ static int a6xx_hwsched_boot(struct adreno_device *adreno_dev) kgsl_pwrscale_wake(device); set_bit(GMU_PRIV_GPU_STARTED, &gmu->flags); + + device->pwrctrl.last_stat_updated = ktime_get(); device->state = KGSL_STATE_ACTIVE; trace_kgsl_pwr_set_state(device, KGSL_STATE_ACTIVE); @@ -589,6 +592,7 @@ static int a6xx_hwsched_first_boot(struct adreno_device *adreno_dev) set_bit(GMU_PRIV_FIRST_BOOT_DONE, &gmu->flags); set_bit(GMU_PRIV_GPU_STARTED, &gmu->flags); + device->pwrctrl.last_stat_updated = ktime_get(); device->state = KGSL_STATE_ACTIVE; trace_kgsl_pwr_set_state(device, KGSL_STATE_ACTIVE); diff --git a/drivers/gpu/msm/adreno_a6xx_rgmu.c b/drivers/gpu/msm/adreno_a6xx_rgmu.c index b16399af6af9..4e82413092e9 100644 --- a/drivers/gpu/msm/adreno_a6xx_rgmu.c +++ b/drivers/gpu/msm/adreno_a6xx_rgmu.c @@ -896,6 +896,7 @@ static int a6xx_boot(struct adreno_device *adreno_dev) set_bit(RGMU_PRIV_GPU_STARTED, &rgmu->flags); + device->pwrctrl.last_stat_updated = ktime_get(); device->state = KGSL_STATE_ACTIVE; trace_kgsl_pwr_set_state(device, KGSL_STATE_ACTIVE); @@ -934,6 +935,7 @@ static void a6xx_rgmu_touch_wakeup(struct adreno_device *adreno_dev) set_bit(RGMU_PRIV_GPU_STARTED, &rgmu->flags); + device->pwrctrl.last_stat_updated = ktime_get(); device->state = KGSL_STATE_ACTIVE; trace_kgsl_pwr_set_state(device, KGSL_STATE_ACTIVE); @@ -1002,6 +1004,7 @@ static int a6xx_first_boot(struct adreno_device *adreno_dev) set_bit(RGMU_PRIV_FIRST_BOOT_DONE, &rgmu->flags); set_bit(RGMU_PRIV_GPU_STARTED, &rgmu->flags); + device->pwrctrl.last_stat_updated = ktime_get(); device->state = KGSL_STATE_ACTIVE; trace_kgsl_pwr_set_state(device, KGSL_STATE_ACTIVE); diff --git a/drivers/gpu/msm/adreno_dispatch.c b/drivers/gpu/msm/adreno_dispatch.c index 7ae90d71130d..5177e74acc10 100644 --- a/drivers/gpu/msm/adreno_dispatch.c +++ b/drivers/gpu/msm/adreno_dispatch.c @@ -304,6 +304,9 @@ static void _retire_timestamp(struct kgsl_drawobj *drawobj) context->id, drawobj->timestamp, !!(drawobj->flags & KGSL_DRAWOBJ_END_OF_FRAME)); + if (drawobj->flags & KGSL_DRAWOBJ_END_OF_FRAME) + atomic64_inc(&context->proc_priv->frame_count); + /* * For A3xx we still get the rptr from the CP_RB_RPTR instead of * rptr scratch out address. At this point GPU clocks turned off. @@ -2355,6 +2358,9 @@ static void retire_cmdobj(struct adreno_device *adreno_dev, context->id, drawobj->timestamp, !!(drawobj->flags & KGSL_DRAWOBJ_END_OF_FRAME)); + if (drawobj->flags & KGSL_DRAWOBJ_END_OF_FRAME) + atomic64_inc(&context->proc_priv->frame_count); + /* * For A3xx we still get the rptr from the CP_RB_RPTR instead of * rptr scratch out address. At this point GPU clocks turned off. diff --git a/drivers/gpu/msm/kgsl.c b/drivers/gpu/msm/kgsl.c index 1b344cd066db..97ee1d058efa 100644 --- a/drivers/gpu/msm/kgsl.c +++ b/drivers/gpu/msm/kgsl.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -1043,6 +1044,24 @@ done: return private; } +int kgsl_gpu_frame_count(pid_t pid, u64 *frame_count) +{ + struct kgsl_process_private *p; + + if (!frame_count) + return -EINVAL; + + p = kgsl_process_private_find(pid); + if (!p) + return -ENOENT; + + *frame_count = atomic64_read(&p->frame_count); + kgsl_process_private_put(p); + + return 0; +} +EXPORT_SYMBOL(kgsl_gpu_frame_count); + static int kgsl_close_device(struct kgsl_device *device) { int result = 0; diff --git a/drivers/gpu/msm/kgsl.h b/drivers/gpu/msm/kgsl.h index 9e4bf99e7163..fc2cb005722c 100644 --- a/drivers/gpu/msm/kgsl.h +++ b/drivers/gpu/msm/kgsl.h @@ -5,12 +5,12 @@ #ifndef __KGSL_H #define __KGSL_H +#include #include #include #include #include #include -#include #include #include "kgsl_gmu_core.h" diff --git a/drivers/gpu/msm/kgsl_compat.h b/drivers/gpu/msm/kgsl_compat.h index a62c3b40d798..85e1871de6ac 100644 --- a/drivers/gpu/msm/kgsl_compat.h +++ b/drivers/gpu/msm/kgsl_compat.h @@ -1,12 +1,12 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* - * Copyright (c) 2013-2017,2019 The Linux Foundation. All rights reserved. + * Copyright (c) 2013-2017,2019,2021 The Linux Foundation. All rights reserved. */ #ifndef __KGSL_COMPAT_H #define __KGSL_COMPAT_H +#include #include -#include #ifdef CONFIG_COMPAT diff --git a/drivers/gpu/msm/kgsl_device.h b/drivers/gpu/msm/kgsl_device.h index e3271ccbe327..3d3cfdab5128 100644 --- a/drivers/gpu/msm/kgsl_device.h +++ b/drivers/gpu/msm/kgsl_device.h @@ -437,6 +437,7 @@ struct kgsl_context { * @fd_count: Counter for the number of FDs for this process * @ctxt_count: Count for the number of contexts for this process * @ctxt_count_lock: Spinlock to protect ctxt_count + * @frame_count: Count for the number of frames processed */ struct kgsl_process_private { unsigned long priv; @@ -460,6 +461,7 @@ struct kgsl_process_private { int fd_count; atomic_t ctxt_count; spinlock_t ctxt_count_lock; + atomic64_t frame_count; }; /** diff --git a/drivers/gpu/msm/kgsl_pwrctrl.c b/drivers/gpu/msm/kgsl_pwrctrl.c index 6b9e43ec330d..7003d76d933f 100644 --- a/drivers/gpu/msm/kgsl_pwrctrl.c +++ b/drivers/gpu/msm/kgsl_pwrctrl.c @@ -4,6 +4,7 @@ */ #include +#include #include #include #include @@ -1878,6 +1879,7 @@ static int _wake(struct kgsl_device *device) kgsl_pwrctrl_clk(device, KGSL_PWRFLAGS_ON, KGSL_STATE_ACTIVE); device->ftbl->deassert_gbif_halt(device); + pwr->last_stat_updated = ktime_get(); /* * No need to turn on/off irq here as it no longer affects * power collapse @@ -2258,3 +2260,42 @@ int kgsl_pwrctrl_set_default_gpu_pwrlevel(struct kgsl_device *device) /* Request adjusted DCVS level */ return device->ftbl->gpu_clock_set(device, pwr->active_pwrlevel); } + +int kgsl_gpu_num_freqs(void) +{ + struct kgsl_device *device = kgsl_get_device(0); + + if (!device) + return -ENODEV; + + return device->pwrctrl.num_pwrlevels; +} +EXPORT_SYMBOL(kgsl_gpu_num_freqs); + +int kgsl_gpu_stat(struct kgsl_gpu_freq_stat *stats, u32 numfreq) +{ + struct kgsl_device *device = kgsl_get_device(0); + struct kgsl_pwrctrl *pwr; + int i; + + if (!device) + return -ENODEV; + + pwr = &device->pwrctrl; + + if (!stats || (numfreq < pwr->num_pwrlevels)) + return -EINVAL; + + mutex_lock(&device->mutex); + kgsl_pwrscale_update_stats(device); + + for (i = 0; i < pwr->num_pwrlevels; i++) { + stats[i].freq = pwr->pwrlevels[i].gpu_freq; + stats[i].active_time = pwr->clock_times[i]; + stats[i].idle_time = pwr->time_in_pwrlevel[i] - pwr->clock_times[i]; + } + mutex_unlock(&device->mutex); + + return 0; +} +EXPORT_SYMBOL(kgsl_gpu_stat); diff --git a/drivers/gpu/msm/kgsl_pwrctrl.h b/drivers/gpu/msm/kgsl_pwrctrl.h index 9b040126c57b..2786c8745bb1 100644 --- a/drivers/gpu/msm/kgsl_pwrctrl.h +++ b/drivers/gpu/msm/kgsl_pwrctrl.h @@ -175,6 +175,10 @@ struct kgsl_pwrctrl { u32 minbw_timeout; /** @ddr_qos_devfreq: Devfreq device for setting DDR qos policy */ struct devfreq *ddr_qos_devfreq; + /** @time_in_pwrlevel: Each pwrlevel active duration in usec */ + u64 time_in_pwrlevel[KGSL_MAX_PWRLEVELS]; + /** @last_stat_updated: The last time stats were updated */ + ktime_t last_stat_updated; }; int kgsl_pwrctrl_init(struct kgsl_device *device); diff --git a/drivers/gpu/msm/kgsl_pwrscale.c b/drivers/gpu/msm/kgsl_pwrscale.c index 870f090c769e..45d6e99a66cb 100644 --- a/drivers/gpu/msm/kgsl_pwrscale.c +++ b/drivers/gpu/msm/kgsl_pwrscale.c @@ -122,6 +122,7 @@ void kgsl_pwrscale_update_stats(struct kgsl_device *device) if (device->state == KGSL_STATE_ACTIVE) { struct kgsl_power_stats stats; + ktime_t cur_time = ktime_get(); device->ftbl->power_stats(device, &stats); device->pwrscale.accum_stats.busy_time += stats.busy_time; @@ -129,6 +130,9 @@ void kgsl_pwrscale_update_stats(struct kgsl_device *device) device->pwrscale.accum_stats.ram_wait += stats.ram_wait; pwrctrl->clock_times[pwrctrl->active_pwrlevel] += stats.busy_time; + pwrctrl->time_in_pwrlevel[pwrctrl->active_pwrlevel] += + ktime_us_delta(cur_time, pwrctrl->last_stat_updated); + pwrctrl->last_stat_updated = cur_time; } } diff --git a/include/linux/msm_kgsl.h b/include/linux/msm_kgsl.h new file mode 100644 index 000000000000..e5950b135dda --- /dev/null +++ b/include/linux/msm_kgsl.h @@ -0,0 +1,52 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2021, The Linux Foundation. All rights reserved. + */ +#ifndef _MSM_KGSL_H +#define _MSM_KGSL_H + +/** + * struct kgsl_gpu_freq_stat - Per GPU freq stat struct + * @freq: GPU frequency in Hz + * @active_time: GPU busy time in usecs + * @idle_time: GPU idle time in usec + */ +struct kgsl_gpu_freq_stat { + u32 freq; + u64 active_time; + u64 idle_time; +}; + +/** + * kgsl_gpu_num_freqs - Get number of available GPU frequencies + * + * Return: number of available frequencies on success or negative error + * on failure + */ +int kgsl_gpu_num_freqs(void); + +/** + * kgsl_gpu_stat - Get per GPU freq stats + * @stats: Array of struct kgsl_gpu_freq_stat to hold stats + * @numfreq: Number of entries in @stats + * + * This function will populate @stats with per freq stats. + * Number of entries in @stats array must be greater or + * equal to value returned by function kgsl_gpu_num_freqs + * + * Return: 0 on success or negative error on failure + */ +int kgsl_gpu_stat(struct kgsl_gpu_freq_stat *stats, u32 numfreq); + +/** + * kgsl_gpu_frame_count - Get number of frames already processed by GPU + * @pid: pid of the process for which frame count is required + * @frame_count: pointer to a u64 to store frame count + * + * Return: zero on success and number of frames processed corresponding + * to @pid in @frame_count or negative error on failure + */ +int kgsl_gpu_frame_count(pid_t pid, u64 *frame_count); + +#endif /* _MSM_KGSL_H */ +