1. 程式人生 > >[RK3288][Android6.0] GPU DVFS控制策略小結

[RK3288][Android6.0] GPU DVFS控制策略小結

Platform: Rockchip
OS: Android 6.0
Kernel: 3.10.92

mali_device_driver 分為兩個部分 : platform_dependent_part 和 common_parts, 參見 mali_kbase_config_rk.c 開頭部分的註釋.

gpu dvfs核心控制在mali_kbase_dvfs.c中.

s_mali_dvfs_level_table 定義gpu所有可變的freq level以及對應的min utilisation和max utilisation.

static struct mali_dvfs_level_t s_mali_dvfs_level_table[] = {
    {100000, 0, 70}
, {160000, 50, 65}, {266000, 60, 78}, {350000, 65, 75}, {400000, 70, 75}, {500000, 90, 100}, };

不過它會被dts中的operating-points所覆蓋

clk_gpu_dvfs_table: clk_gpu {
    operating-points = <
        /* KHz uV */
        200000 1200000
        300000 1200000
        400000 1200000
        >;
};

gpu dvfs執行時, common parts (metrics system)會週期地(20ms)通知回撥 platform_dependent_part 中的函式 kbase_platform_dvfs_event(), 並傳入當前的 gpu utilization.

dvfs_callback -> kbase_pm_get_dvfs_action -> kbase_platform_dvfs_event

int kbase_platform_dvfs_event(struct kbase_device *kbdev,
                  u32 utilisation, /* calculated_utilisation. */
                  u32 util_gl_share_no_use,
                  u32 util_cl_share_no_use[2])
{
    struct
rk_dvfs_t *dvfs = get_rk_dvfs(kbdev); dvfs->utilisation = utilisation; if (dvfs->is_enabled) { /* run 'mali_dvfs_work' in 'mali_dvfs_wq', on cpu0. */ queue_work_on(0, dvfs->mali_dvfs_wq, &(dvfs->mali_dvfs_work)); } return 1; }

該 event 被轉發到 mali_dvfs_event_proc() 中處理.

static void mali_dvfs_event_proc(struct work_struct *w)
{
    int ret = 0;
    struct rk_dvfs_t *dvfs = work_to_dvfs(w);
    int temp = rockchip_tsadc_get_temp(1, 0);

    if (INVALID_TEMP == temp) {
        D("got invalid temp, reset to 0.");
        temp = 0;
    }
    dvfs->sum_of_temps += temp;
    dvfs->times_of_temp_measures++;

    if (dvfs->times_of_temp_measures >= NUM_OF_TEMP_MEASURES_IN_A_SESSION) {
        dvfs->temp = dvfs->sum_of_temps
            / NUM_OF_TEMP_MEASURES_IN_A_SESSION;

        dvfs->times_of_temp_measures = 0;
        dvfs->sum_of_temps = 0;
    }

    /*-------------------------------------------------------*/

    mutex_lock(&(dvfs->dvfs_mutex));

    if (is_overheated(dvfs)) {
        if (could_jump_down(dvfs)) {
            I("to jump down for overheated, temp:%d.",
              dvfs->temp);
            ret = jump_down_actually(dvfs);
            if (ret)
                E("fail to jump down, ret:%d.", ret);
        } else {
            W("overheated! temp:%d, but can't jump down anymore.",
              dvfs->temp);
        }

        dvfs->temp = 0;
        goto EXIT;
    }

    /* If calculated_utilisation asks current_level to jump up,
     * and current_level could jump up,
     * then ....  */
    if (dvfs->utilisation > get_util_max_threshold_of_curr_level(dvfs) &&
        could_jump_up(dvfs)) {
        inc_requests_to_jump_up(dvfs);

        if (are_enough_jump_up_requests(dvfs)) {
            V("to jump up to highest, util:%d, curr_level:%d.",
              dvfs->utilisation,
              get_current_level(dvfs));
            ret = jump_up_to_highest_actually(dvfs);
            if (ret) {
                E("fail to jump up, ret:%d.", ret);
                goto EXIT;
            }

            reset_requests_to_jump_up(dvfs);
        }

        reset_requests_to_jump_down(dvfs);
        goto EXIT;
    } else if (dvfs->utilisation
            < get_util_min_threshold_of_curr_level(dvfs) &&
           could_jump_down(dvfs)) {
        inc_requests_to_jump_down(dvfs);

        if (are_enough_jump_down_requests(dvfs)) {
            V("to jump down actually, util:%d, curr_level:%d",
              dvfs->utilisation,
              get_current_level(dvfs));
            jump_down_actually(dvfs);

            reset_requests_to_jump_down(dvfs);
        }

        reset_requests_to_jump_up(dvfs);
        goto EXIT;
    } else {
        reset_requests_to_jump_down(dvfs);
        reset_requests_to_jump_up(dvfs);

        V("stay in current_level, util:%d, curr_level:%d.",
                dvfs->utilisation,
                get_current_level(dvfs));
    }

EXIT:
    mutex_unlock(&(dvfs->dvfs_mutex));
}

a. 如果當前溫度過高,那麼執行jump_down_actually()下調頻率.
b. 如果當前utilization 大於 max_utilization_of_current_dvfs_level, 則直接上跳到最高頻率(jump_up_to_highest_actually() ).不過要滿足NUM_OF_REQUESTS_TO_PERFORM_ACTUAL_JUMP_UP的次數才上跳.
c. 如果當前utilization 小於max_utilization_of_current_dvfs_level,則一級級下跳(jump_down_actually()).不過要滿足NUM_OF_REQUESTS_TO_PERFORM_ACTUAL_JUMP_DOWN的次數才下跳.

如果覺得目前上跳策略太激進, 則可以在 mali_dvfs_event_proc() 進一步修改, 比如每次只上跳一級, 而不是直接到 highest.