DVFS(Dynamic Voltage And Frequency Scaling)动态电压和频率调节,这里主要研究GPU频率动态的调节。
和CPU采用cpufreq框架动态调节频率类似,GPU这块采用了Linux devfreq框架。
Devfreq有四种对频率的管理策略governor:
1、 performance:GPU会固定工作在其支持的最高频率上,以追求最高性能。
2、 powersave:GPU会固定工作在其支持的最低频率上,以追求最低的功耗。
3、 userspace:早期的管理策略,系统将变频策略的决策交给了用户态应用程序,并提供相应的接口供其使用。
4、 ondemand:userspace 是用户态的检测,效率低,而 ondemand 则是一个完全在内核态下工作并且能够以更加细粒度的时间间隔对系统负载情况进行采样分析的 governor 。
Devfreq只提供了实现变频的框架,只是机制,而具体如何决定变频的策略则是各硬件厂商根据其硬件特性来决定的。
Qualcomm的GPU则使用的是他们自己的governor:msm-adreno-tz,他和ondemand有点相似,是在内核态根据GPU的负载来动态决定如何变频的。
我们来看一下他们的实现机制。
首先在初始化的过程中,msm-adreno-tz先调用devfreq_add_governor(),向devfreq框架中添加一个governor。
随后,Adreno在初始化的过程中调用devfreq_add_device(“msm-adreno-tz”),并选定其使用的governor,它同时像devfreq框架提供了几个回调函数,这些函数在devfreq_dev_profile结构体中:
.target =kgsl_devfreq_target;//设置调整后的频率
.get_dev_status= kgsl_devfreq_get_dev_status;//获得GPU的负载
.get_cur_freq= kgsl_devfreq_get_cur_freq;//获得GPU当前的频率值
在匹配完成后,devfreq框架发送DEVFREQ_GOV_START消息到msm-adreno-tz,这样msm-adreno-tz就会注册notifier到Adreno,这样 Adreno就可以使用msm-aderno-tz来动态调整频率了。
在msm-adreno-tz初始化阶段,会向Adreno注册一个回调函数tz_notify(),这样Adreno就可以通过tz_notify来通知msm-adreno-tz动态调整频率了,具体流程是:
adreno_dispatcher_work
-->kgsl_pwrscale_idle
-->queue_work(devfreq_notify_ws)
-->do_devfreq_notify(ADRENO_DEVFREQ_NOTIFY_RETIRE)
-->tz_notify()
-->update_devfreq(devfreq)
-->profile->get_dev_status()
-->profile.target()
adreno_dispatcher_work()相当于是一个分配任务的回调接口,可以理解是个Loop,这样流程就可以串起来了。
msm-adreno-tz动态调整频率使用devfreq框架提供的update_devfreq()函数,该函数通过get_target_freq()回调函数来获得要调整的频率值。在performance中,get_target_freq()返回的是max_freq,在powersave中返回的是min_freq,在我们的msm-adreno-tz中,则会调用profile->get_dev_status()回调函数,从Adreno驱动中获取GPU的负载,并通过一定算法(__secure_tz_entry3??)来计算出频率值,并通过profile->target()回调函数,改变Adreno的频率,至此一个Loop就完成了,接下来就是一直Loop直至系统待机。
高通给的可以手动调节GPU频率的接口为:
echo1 > /sys/class/kgsl/kgsl-3d0/force_clk_on
echo 10000000 > /sys/class/kgsl/kgsl-3d0/idle_timer
echo performance > /sys/class/kgsl/kgsl-3d0/devfreq/governor
echo<max freq> > /sys/class/kgsl/kgsl-3d0/gpucl
具体理解为是:
1、echo 1 > force_clk_on是设置KGSL_PWRFLAGS_CLK_ON这个power_flags。使用echo freq > gpuclk时,是通过kgsl_pwrctrl_pwrlevel_change设置频率,它会判断KGSL_PWRFLAGS_CLK_ON这个power_flags,如果没有这个flag,可能就不会真正的设置频率,不过我测试了一下,即使不用这个命令,在大多数情况下也是可以工作的。也就是说,它会强制使得接下来第@4步的设置生效。
2、echo 10000000 > idle_timer,,设置interval_timeout,默认值是80ms。
当系统启动的时候,会初始化一个idle_timer-->kgsl_timer,这个timer会执行kgsl_idle_check()用于检测GPU是否处于idle状态,如果是,则会调用:
kgsl_pwrctrl_sleep()-->devfreq_suspend_device()
-->governor->event_handler(DEVFREQ_GOV_SUSPEND)
-->msm-adreno-tz/performance,stop
接下来如果有系统调用至KGSL,则会唤醒Adreno:
kgsl_pwrscale_wake()
--> devfreq_resume_device()
-->governor->event_handler(DEVFREQ_GOV_RESUME)
-->msm-adreno-tz/performance,start
而在governor restart的过程中,会重新初始化GPU所对应的频率,这样即使我们设置了我们想要的频率,最后也很快就被冲掉了,所以要把该interval_timeout设置为很大。
3、echo performance > devfreq/governor, 这时devfreq会为Adreno重新选择governor,也就是performance。这时它会使用GPU所支持的最大频率,而不考虑系统的负载。默认的msm-adreno-tz机制会不停的动态更新频率,即使我们设置过频率也会被覆盖掉,这就是为什么直接设置频率无效的原因。
而如果改成performance机制,这样它会使得Adreno动态的调整机制无效,我们的设置才会生效。
4、echo freq > gpuclk,简单的设置GPU的频率,一般是在200000 000, 320 000 000和450 000 000之间,如果不对,就会就近选择这三者之一。在前面设置做铺垫的情况下,idle_interval被设置成了很大,这样边不会suspend、resume,我们设置的gpuclk才不会被重新设置,并且do_devfreq_notify()也不起了作用,所以可以用我们设置的频率进行功耗及跑分测试了。
转自:http://blog.csdn.net/crazyrandom/article/details/37912393