一. GPU DVFS调频过程
在ODROID源码目录/kernel/hardkernel/odroidxu3/drivers/gpu/arm/midgard/platform/gpu_dvfs_handler.c
文件中定义了gpu_dvfs_event_proc
函数,其作用即是对GPU进行调频,其内容如下:
static void gpu_dvfs_event_proc(struct work_struct *q)
{
int freq = 0;
struct kbase_device *kbdev = pkbdev;
struct exynos_context *platform;
platform = (struct exynos_context *) kbdev->platform_context;
if (!platform)
return;
mutex_lock(&platform->gpu_dvfs_handler_lock);
if (gpu_control_state_set(kbdev, GPU_CONTROL_IS_POWER_ON, 0)) {
freq = gpu_get_target_freq();
gpu_set_target_freq(freq);
}
mutex_unlock(&platform->gpu_dvfs_handler_lock);
}
//gpu_dvfs_handler.c:108
//将gpu_dvfs_event_proc定义成名称为gpu_dvfs_work的work
static DECLARE_WORK(gpu_dvfs_work, gpu_dvfs_event_proc);
//gpu_dvfs_handler.c:149
//将gpu_dvfs_work放到cpu0上执行,platform->dvfs_wq:workqueue to use,gpu_dvfs_work:work to queue
if (platform->dvfs_wq)
queue_work_on(0, platform->dvfs_wq, &gpu_dvfs_work);
可以看出,gpu_dvfs_event_proc
函数先调用gpu_get_target_freq()
获取将要调至的频率,再使用gpu_set_target_freq(freq)
进行调频。
另外,在ODROID源码目录/kernel/hardkernel/odroidxu3/drivers/gpu/arm/midgard/platform/gpu_dvfs_handler.c
文件中也定义了gpu_get_target_freq
函数,其定义如下:
static int gpu_get_target_freq(void)
{
int freq;
struct kbase_device *kbdev = pkbdev;
struct exynos_context *platform;
platform = (struct exynos_context *) kbdev->platform_context;
if (!platform)
return -ENODEV;
#ifdef CONFIG_MALI_T6XX_DVFS
gpu_dvfs_decide_next_level(kbdev, platform->utilization);
#endif /* CONFIG_MALI_T6XX_DVFS */
freq = platform->table[platform->step].clock;
#ifdef CONFIG_MALI_T6XX_DVFS
if ((platform->max_lock > 0) && (freq > platform->max_lock))
freq = platform->max_lock;
else if ((platform->min_lock > 0) && (freq < platform->min_lock))
freq = platform->min_lock;
#endif /* CONFIG_MALI_T6XX_DVFS */
return freq;
}
gpu_get_target_freq
函数中调用了gpu_dvfs_decide_next_level
函数,其定义在ODROID源码目录/kernel/hardkernel/odroidxu3/drivers/gpu/arm/midgard/platform/gpu_dvfs_governor.c
文件中,内容如下:
int gpu_dvfs_decide_next_level(struct kbase_device *kbdev, int utilization)
{
unsigned long flags;
struct exynos_context *platform = (struct exynos_context *) kbdev->platform_context;
if (!platform)
return -ENODEV;
spin_lock_irqsave(&platform->gpu_dvfs_spinlock, flags);
gpu_dvfs_get_next_freq(kbdev, utilization);
spin_unlock_irqrestore(&platform->gpu_dvfs_spinlock, flags);
return 0;
}
gpu_dvfs_decide_next_level
函数中调用了gpu_dvfs_get_next_freq
函数,gpu_dvfs_get_next_freq
是一个函数指针变量,其也定义在ODROID源码目录/kernel/hardkernel/odroidxu3/drivers/gpu/arm/midgard/platform/gpu_dvfs_governor.c
文件中,其内容如下:
typedef void (*GET_NEXT_FREQ)(struct kbase_device *kbdev, int utilization);
GET_NEXT_FREQ gpu_dvfs_get_next_freq;
在gpu_dvfs_governor.c
文件中的gpu_dvfs_governor_init
函数内,我们可以看到如下语句:
#ifdef CONFIG_MALI_T6XX_DVFS
switch (governor_type) {
case G3D_DVFS_GOVERNOR_DEFAULT:
gpu_dvfs_get_next_freq = (GET_NEXT_FREQ)&gpu_dvfs_governor_default;
platform->table = gpu_dvfs_infotbl_default;
platform->table_size = GPU_DVFS_TABLE_SIZE(gpu_dvfs_infotbl_default);
#ifdef CONFIG_DYNIMIC_ABB
platform->devfreq_g3d_asv_abb = gpu_abb_infobl_default;
#endif
由此可知,实际决定GPU频率的函数是gpu_dvfs_governor_default
函数(因为gpu_dvfs_get_next_freq
指向gpu_dvfs_governor_default
),其也定义在gpu_dvfs_governor.c
中,内容如下:
#ifdef CONFIG_MALI_T6XX_DVFS
static int gpu_dvfs_governor_default(struct kbase_device *kbdev, int utilization)
{
struct exynos_context *platform;
platform = (struct exynos_context *) kbdev->platform_context;
if (!platform)
return -ENODEV;
if ((platform->step < platform->table_size-1) &&
(utilization > platform->table[platform->step].max_threshold)) {
platform->step++;
platform->down_requirement = platform->table[platform->step].stay_count;
DVFS_ASSERT(platform->step < platform->table_size);
} else if ((platform->step > 0) && (utilization < platform->table[platform->step].min_threshold)) {
DVFS_ASSERT(platform->step > 0);
platform->down_requirement--;
if (platform->down_requirement == 0) {
platform->step--;
platform->down_requirement = platform->table[platform->step].stay_count;
}
} else {
platform->down_requirement = platform->table[platform->step].stay_count;
}
return 0;
}
因此我们也只需要在gpu_dvfs_governor.c
中写一个类似于static int gpu_dvfs_governor_custom(struct kbase_device *kbdev, int utilization)
的函数,并将gpu_dvfs_get_next_freq
函数指针指向我们自己所定义的调频函数即可。
二. 关于GPU调节频率表
在gpu_dvfs_governor_init
函数中,有如下定义:
case G3D_DVFS_GOVERNOR_DEFAULT:
gpu_dvfs_get_next_freq = (GET_NEXT_FREQ)&gpu_dvfs_governor_default;
platform->table = gpu_dvfs_infotbl_default;
platform->table_size = GPU_DVFS_TABLE_SIZE(gpu_dvfs_infotbl_default);
补充:
#define GPU_DVFS_TABLE_SIZE(X) ARRAY_SIZE(X)
gpu_dvfs_infotbl_default
的定义如下:
static gpu_dvfs_info gpu_dvfs_infotbl_default[] = {
/* vol,clk,min,max,down stay, pm_qos mem, pm_qos int, pm_qos cpu_kfc_min, pm_qos cpu_egl_max */
#if SOC_NAME == 5422
{812500, 177, 0, 90, 2, 0, 275000, 222000, 0, CPU_MAX},
{862500, 266, 60, 90, 1, 0, 413000, 222000, 0, CPU_MAX},
{912500, 350, 70, 90, 1, 0, 728000, 333000, 0, CPU_MAX},
{962500, 420, 78, 90, 1, 0, 825000, 400000, 0, CPU_MAX},
{1000000, 480, 90, 99, 1, 0, 825000, 400000, 1000000, 1600000},
#ifdef CONFIG_SOC_EXYNOS5422_REV_0
{1037500, 543, 99, 100, 1, 0, 825000, 400000, 1000000, 1600000},
#else
{1037500, 533, 99, 100, 1, 0, 825000, 400000, 1000000, 1600000},
#endif /* CONFIG_SOC_EXYNOS5422_REV_0 */
gpu_dvfs_info
的定义如下:
typedef struct _gpu_dvfs_info {
unsigned int voltage;
unsigned int clock;
int min_threshold;
int max_threshold;
int stay_count;
unsigned long long time;
int mem_freq;
int int_freq;
int cpu_freq;
int cpu_max_freq;
} gpu_dvfs_info;
三. 关于GPU_DVFS定时器
//gpu_dvfs_handler.c:214
hrtimer_start(&kbdev->pm.metrics.timer, HR_TIMER_DELAY_MSEC(platform->polling_speed), HRTIMER_MODE_REL);
//mali_kbase_platform.c:75
platform->polling_speed = 100;
故而,每隔100毫秒进行一次GPU DVFS调频操作。