From 9280340fe029a23c170189dacdf9b2c734e9dfdd Mon Sep 17 00:00:00 2001 From: David Guillen Fandos <david@davidgf.net> Date: Wed, 12 May 2021 02:16:25 +0200 Subject: [PATCH] CPU governor/frequency part 2 This adds managed policies and settings to store them and reload them at startup. Only for Lakka builds! --- configuration.c | 10 ++ configuration.h | 8 ++ intl/msg_hash_us.h | 68 ++++++++++++ menu/cbs/menu_cbs_get_value.c | 90 ++++++++++++++++ menu/cbs/menu_cbs_left.c | 87 ++++++++++++++++ menu/cbs/menu_cbs_right.c | 89 +++++++++++++++- menu/cbs/menu_cbs_sublabel.c | 20 +++- menu/menu_displaylist.c | 65 ++++++++++-- menu/menu_driver.h | 2 + misc/cpufreq/cpufreq.c | 189 +++++++++++++++++++++++++++++++--- misc/cpufreq/cpufreq.h | 46 ++++++++- msg_hash.h | 19 ++++ retroarch.c | 24 +++++ 13 files changed, 691 insertions(+), 26 deletions(-) diff --git a/configuration.c b/configuration.c index ef622c9c9c..36f71d72ca 100644 --- a/configuration.c +++ b/configuration.c @@ -1279,6 +1279,10 @@ static struct config_array_setting *populate_settings_array(settings_t *settings SETTING_ARRAY("discord_app_id", settings->arrays.discord_app_id, true, DEFAULT_DISCORD_APP_ID, true); SETTING_ARRAY("ai_service_url", settings->arrays.ai_service_url, true, DEFAULT_AI_SERVICE_URL, true); SETTING_ARRAY("crt_switch_timings", settings->arrays.crt_switch_timings, false, NULL, true); +#ifdef HAVE_LAKKA + SETTING_ARRAY("cpu_main_gov", settings->arrays.cpu_main_gov, false, NULL, true); + SETTING_ARRAY("cpu_menu_gov", settings->arrays.cpu_menu_gov, false, NULL, true); +#endif *size = count; @@ -2113,6 +2117,12 @@ static struct config_uint_setting *populate_settings_uint( SETTING_UINT("video_black_frame_insertion", &settings->uints.video_black_frame_insertion, true, DEFAULT_BLACK_FRAME_INSERTION, false); +#ifdef HAVE_LAKKA + SETTING_UINT("cpu_scaling_mode", &settings->uints.cpu_scaling_mode, true, 0, false); + SETTING_UINT("cpu_min_freq", &settings->uints.cpu_min_freq, true, 1, false); + SETTING_UINT("cpu_max_freq", &settings->uints.cpu_max_freq, true, ~0U, false); +#endif + *size = count; return tmp; diff --git a/configuration.h b/configuration.h index 3dcdf9a8ee..055b787ebd 100644 --- a/configuration.h +++ b/configuration.h @@ -288,6 +288,12 @@ typedef struct settings unsigned core_updater_auto_backup_history_size; unsigned video_black_frame_insertion; unsigned quit_on_close_content; + +#ifdef HAVE_LAKKA + unsigned cpu_scaling_mode; + unsigned cpu_min_freq; + unsigned cpu_max_freq; +#endif } uints; struct @@ -387,6 +393,8 @@ typedef struct settings char crt_switch_timings[255]; #ifdef HAVE_LAKKA char timezone[TIMEZONE_LENGTH]; + char cpu_main_gov[32]; + char cpu_menu_gov[32]; #endif } arrays; diff --git a/intl/msg_hash_us.h b/intl/msg_hash_us.h index 50f84e7af5..4856e6b048 100644 --- a/intl/msg_hash_us.h +++ b/intl/msg_hash_us.h @@ -12092,6 +12092,58 @@ MSG_HASH( MENU_ENUM_LABEL_VALUE_CPU_POLICY_ENTRY, "Policy" ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_CPU_PERF_MODE, + "Governing Mode" + ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_CPU_PERF_MODE_MANUAL, + "Manual" + ) +MSG_HASH( + MENU_ENUM_SUBLABEL_VALUE_CPU_PERF_MODE_MANUAL, + "Allows to manually tweak every detail in every CPU: governor, frequencies, etc. Only recommended for advanced users." + ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_CPU_PERF_MODE_MANAGED_PERF, + "Performance (Managed)" + ) +MSG_HASH( + MENU_ENUM_SUBLABEL_VALUE_CPU_PERF_MODE_MANAGED_PERF, + "Default and recommended mode. Maximum performance while playing while saving power when paused or browsing menus." + ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_CPU_PERF_MODE_MANAGED_PER_CONTEXT, + "Custom Managed" + ) +MSG_HASH( + MENU_ENUM_SUBLABEL_VALUE_CPU_PERF_MODE_MANAGED_PER_CONTEXT, + "Allows to choose what governors to use in menus and during gameplay. Performance, Ondemand or Schedutil are recommended during gameplay." + ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_CPU_PERF_MODE_MAX_PERF, + "Maximum Performance" + ) +MSG_HASH( + MENU_ENUM_SUBLABEL_VALUE_CPU_PERF_MODE_MAX_PERF, + "Always maximum performance: highest frequencies for best experience." + ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_CPU_PERF_MODE_MIN_POWER, + "Minimum Power" + ) +MSG_HASH( + MENU_ENUM_SUBLABEL_VALUE_CPU_PERF_MODE_MIN_POWER, + "Use the lowest frequency available to save power. Useful on battery powered devices but performance will be significantly reduced." + ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_CPU_PERF_MODE_BALANCED, + "Balanced" + ) +MSG_HASH( + MENU_ENUM_SUBLABEL_VALUE_CPU_PERF_MODE_BALANCED, + "Adapts to the current workload. Works well with most devices and emulators and helps saving power. Demanding games and cores might suffer a performance drop on some devices." + ) MSG_HASH( MENU_ENUM_LABEL_VALUE_CPU_POLICY_MIN_FREQ, "Minimum Frequency" @@ -12100,10 +12152,26 @@ MSG_HASH( MENU_ENUM_LABEL_VALUE_CPU_POLICY_MAX_FREQ, "Maximum Frequency" ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_CPU_MANAGED_MIN_FREQ, + "Minimum Core Frequency" + ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_CPU_MANAGED_MAX_FREQ, + "Maximum Core Frequency" + ) MSG_HASH( MENU_ENUM_LABEL_VALUE_CPU_POLICY_GOVERNOR, "CPU Governor" ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_CPU_POLICY_CORE_GOVERNOR, + "Core Governor" + ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_CPU_POLICY_MENU_GOVERNOR, + "Menu Governor" + ) MSG_HASH( MENU_ENUM_LABEL_VALUE_PAL60_ENABLE, "Use PAL60 Mode" diff --git a/menu/cbs/menu_cbs_get_value.c b/menu/cbs/menu_cbs_get_value.c index f2a2ca9db7..f5befe000c 100644 --- a/menu/cbs/menu_cbs_get_value.c +++ b/menu/cbs/menu_cbs_get_value.c @@ -518,6 +518,50 @@ static void menu_action_setting_disp_set_label_core_manager_entry( #ifndef HAVE_LAKKA_SWITCH #ifdef HAVE_LAKKA +static void menu_action_setting_disp_cpu_gov_mode( + file_list_t* list, + unsigned *w, unsigned type, unsigned i, + const char *label, + char *s, size_t len, + const char *path, + char *s2, size_t len2) +{ + const char *alt = list->list[i].alt + ? list->list[i].alt + : list->list[i].path; + enum cpu_scaling_mode mode = get_cpu_scaling_mode(NULL); + + if (alt) + strlcpy(s2, alt, len2); + + strlcpy(s, msg_hash_to_str( + MENU_ENUM_LABEL_VALUE_CPU_PERF_MODE_MANAGED_PERF + (int)mode), len); +} + +static void menu_action_setting_disp_cpu_gov_choose( + file_list_t* list, + unsigned *w, unsigned type, unsigned i, + const char *label, + char *s, size_t len, + const char *path, + char *s2, size_t len2) +{ + const char *alt = list->list[i].alt + ? list->list[i].alt + : list->list[i].path; + int fnum = atoi(list->list[i].label); + cpu_scaling_opts_t opts; + enum cpu_scaling_mode mode = get_cpu_scaling_mode(&opts); + + if (alt) + strlcpy(s2, alt, len2); + + if (!fnum) + strlcpy(s, opts.main_policy, len); + else + strlcpy(s, opts.menu_policy, len); +} + static void menu_action_setting_disp_set_label_cpu_policy( file_list_t* list, unsigned *w, unsigned type, unsigned i, @@ -542,6 +586,39 @@ static void menu_action_setting_disp_set_label_cpu_policy( MENU_ENUM_LABEL_VALUE_CPU_POLICY_ENTRY), policyid); } +static void menu_action_cpu_managed_freq_label( + file_list_t* list, + unsigned *w, unsigned type, unsigned i, + const char *label, + char *s, size_t len, + const char *path, + char *s2, size_t len2) +{ + uint32_t freq = 0; + cpu_scaling_opts_t opts; + enum cpu_scaling_mode mode = get_cpu_scaling_mode(&opts); + + switch (type) { + case MENU_SETTINGS_CPU_MANAGED_SET_MINFREQ: + strlcpy(s2, msg_hash_to_str( + MENU_ENUM_LABEL_VALUE_CPU_MANAGED_MIN_FREQ), len2); + freq = opts.min_freq; + break; + case MENU_SETTINGS_CPU_MANAGED_SET_MAXFREQ: + strlcpy(s2, msg_hash_to_str( + MENU_ENUM_LABEL_VALUE_CPU_MANAGED_MAX_FREQ), len2); + freq = opts.max_freq; + break; + }; + + if (freq == 1) + strlcpy(s, "Min.", len); + else if (freq == ~0U) + strlcpy(s, "Max.", len); + else + snprintf(s, len, "%u MHz", freq / 1000); +} + static void menu_action_cpu_freq_label( file_list_t* list, unsigned *w, unsigned type, unsigned i, @@ -1786,6 +1863,15 @@ static int menu_cbs_init_bind_get_string_representation_compare_label( break; #ifndef HAVE_LAKKA_SWITCH #ifdef HAVE_LAKKA + case MENU_ENUM_LABEL_CPU_PERF_MODE: + BIND_ACTION_GET_VALUE(cbs, + menu_action_setting_disp_cpu_gov_mode); + break; + case MENU_ENUM_LABEL_CPU_POLICY_CORE_GOVERNOR: + case MENU_ENUM_LABEL_CPU_POLICY_MENU_GOVERNOR: + BIND_ACTION_GET_VALUE(cbs, + menu_action_setting_disp_cpu_gov_choose); + break; case MENU_ENUM_LABEL_CPU_POLICY_ENTRY: BIND_ACTION_GET_VALUE(cbs, menu_action_setting_disp_set_label_cpu_policy); @@ -1794,6 +1880,10 @@ static int menu_cbs_init_bind_get_string_representation_compare_label( case MENU_ENUM_LABEL_CPU_POLICY_MAX_FREQ: BIND_ACTION_GET_VALUE(cbs, menu_action_cpu_freq_label); break; + case MENU_ENUM_LABEL_CPU_MANAGED_MIN_FREQ: + case MENU_ENUM_LABEL_CPU_MANAGED_MAX_FREQ: + BIND_ACTION_GET_VALUE(cbs, menu_action_cpu_managed_freq_label); + break; case MENU_ENUM_LABEL_CPU_POLICY_GOVERNOR: BIND_ACTION_GET_VALUE(cbs, menu_action_cpu_governor_label); break; diff --git a/menu/cbs/menu_cbs_left.c b/menu/cbs/menu_cbs_left.c index bbe62cc687..66a85c1990 100644 --- a/menu/cbs/menu_cbs_left.c +++ b/menu/cbs/menu_cbs_left.c @@ -672,6 +672,82 @@ static int manual_content_scan_core_name_left(unsigned type, const char *label, } #ifdef HAVE_LAKKA +static int cpu_policy_mode_change(unsigned type, const char *label, + bool wraparound) +{ + bool refresh = false; + enum cpu_scaling_mode mode = get_cpu_scaling_mode(NULL); + if (mode != CPUSCALING_MANAGED_PERFORMANCE) + mode--; + set_cpu_scaling_mode(mode, NULL); + menu_entries_ctl(MENU_ENTRIES_CTL_SET_REFRESH, &refresh); + return 0; +} + +static int cpu_policy_freq_managed_tweak(unsigned type, const char *label, + bool wraparound) +{ + bool refresh = false; + cpu_scaling_opts_t opts; + enum cpu_scaling_mode mode = get_cpu_scaling_mode(&opts); + + switch (type) { + case MENU_SETTINGS_CPU_MANAGED_SET_MINFREQ: + opts.min_freq = get_cpu_scaling_next_frequency_limit( + opts.min_freq, -1); + set_cpu_scaling_mode(mode, &opts); + break; + case MENU_SETTINGS_CPU_MANAGED_SET_MAXFREQ: + opts.max_freq = get_cpu_scaling_next_frequency_limit( + opts.max_freq, -1); + set_cpu_scaling_mode(mode, &opts); + break; + }; + + return 0; +} + +static int cpu_policy_freq_managed_gov(unsigned type, const char *label, + bool wraparound) +{ + int pidx; + bool refresh = false; + cpu_scaling_opts_t opts; + enum cpu_scaling_mode mode = get_cpu_scaling_mode(&opts); + cpu_scaling_driver_t **drivers = get_cpu_scaling_drivers(false); + + /* Using drivers[0] governors, should be improved */ + if (!drivers || !drivers[0]) + return -1; + + switch (atoi(label)) { + case 0: + pidx = string_list_find_elem(drivers[0]->available_governors, + opts.main_policy); + if (pidx > 1) + { + strlcpy(opts.main_policy, + drivers[0]->available_governors->elems[pidx-2].data, + sizeof(opts.main_policy)); + set_cpu_scaling_mode(mode, &opts); + } + break; + case 1: + pidx = string_list_find_elem(drivers[0]->available_governors, + opts.menu_policy); + if (pidx > 1) + { + strlcpy(opts.menu_policy, + drivers[0]->available_governors->elems[pidx-2].data, + sizeof(opts.menu_policy)); + set_cpu_scaling_mode(mode, &opts); + } + break; + }; + + return 0; +} + static int cpu_policy_freq_tweak(unsigned type, const char *label, bool wraparound) { @@ -1012,11 +1088,22 @@ static int menu_cbs_init_bind_left_compare_label(menu_file_list_cbs_t *cbs, break; #ifndef HAVE_LAKKA_SWITCH #ifdef HAVE_LAKKA + case MENU_ENUM_LABEL_CPU_PERF_MODE: + BIND_ACTION_LEFT(cbs, cpu_policy_mode_change); + break; case MENU_ENUM_LABEL_CPU_POLICY_MAX_FREQ: case MENU_ENUM_LABEL_CPU_POLICY_MIN_FREQ: case MENU_ENUM_LABEL_CPU_POLICY_GOVERNOR: BIND_ACTION_LEFT(cbs, cpu_policy_freq_tweak); break; + case MENU_ENUM_LABEL_CPU_MANAGED_MIN_FREQ: + case MENU_ENUM_LABEL_CPU_MANAGED_MAX_FREQ: + BIND_ACTION_LEFT(cbs, cpu_policy_freq_managed_tweak); + break; + case MENU_ENUM_LABEL_CPU_POLICY_CORE_GOVERNOR: + case MENU_ENUM_LABEL_CPU_POLICY_MENU_GOVERNOR: + BIND_ACTION_LEFT(cbs, cpu_policy_freq_managed_gov); + break; #endif #endif default: diff --git a/menu/cbs/menu_cbs_right.c b/menu/cbs/menu_cbs_right.c index 728c270ff0..aa68f6f863 100644 --- a/menu/cbs/menu_cbs_right.c +++ b/menu/cbs/menu_cbs_right.c @@ -789,8 +789,84 @@ static int manual_content_scan_core_name_right(unsigned type, const char *label, return 0; } -#ifdef HAVE_LAKKA_SWITCH +#ifndef HAVE_LAKKA_SWITCH #ifdef HAVE_LAKKA +static int cpu_policy_mode_change(unsigned type, const char *label, + bool wraparound) +{ + bool refresh = false; + enum cpu_scaling_mode mode = get_cpu_scaling_mode(NULL); + if (mode != CPUSCALING_MANUAL) + mode++; + set_cpu_scaling_mode(mode, NULL); + menu_entries_ctl(MENU_ENTRIES_CTL_SET_REFRESH, &refresh); + return 0; +} + +static int cpu_policy_freq_managed_tweak(unsigned type, const char *label, + bool wraparound) +{ + bool refresh = false; + cpu_scaling_opts_t opts; + enum cpu_scaling_mode mode = get_cpu_scaling_mode(&opts); + + switch (type) { + case MENU_SETTINGS_CPU_MANAGED_SET_MINFREQ: + opts.min_freq = get_cpu_scaling_next_frequency_limit( + opts.min_freq, 1); + set_cpu_scaling_mode(mode, &opts); + break; + case MENU_SETTINGS_CPU_MANAGED_SET_MAXFREQ: + opts.max_freq = get_cpu_scaling_next_frequency_limit( + opts.max_freq, 1); + set_cpu_scaling_mode(mode, &opts); + break; + }; + + return 0; +} + +static int cpu_policy_freq_managed_gov(unsigned type, const char *label, + bool wraparound) +{ + int pidx; + bool refresh = false; + cpu_scaling_opts_t opts; + enum cpu_scaling_mode mode = get_cpu_scaling_mode(&opts); + cpu_scaling_driver_t **drivers = get_cpu_scaling_drivers(false); + + /* Using drivers[0] governors, should be improved */ + if (!drivers || !drivers[0]) + return -1; + + switch (atoi(label)) { + case 0: + pidx = string_list_find_elem(drivers[0]->available_governors, + opts.main_policy); + if (pidx && pidx + 1 < drivers[0]->available_governors->size) + { + strlcpy(opts.main_policy, + drivers[0]->available_governors->elems[pidx].data, + sizeof(opts.main_policy)); + set_cpu_scaling_mode(mode, &opts); + } + break; + case 1: + pidx = string_list_find_elem(drivers[0]->available_governors, + opts.menu_policy); + if (pidx && pidx + 1 < drivers[0]->available_governors->size) + { + strlcpy(opts.menu_policy, + drivers[0]->available_governors->elems[pidx].data, + sizeof(opts.menu_policy)); + set_cpu_scaling_mode(mode, &opts); + } + break; + }; + + return 0; +} + static int cpu_policy_freq_tweak(unsigned type, const char *label, bool wraparound) { @@ -1132,11 +1208,22 @@ static int menu_cbs_init_bind_right_compare_label(menu_file_list_cbs_t *cbs, break; #ifndef HAVE_LAKKA_SWITCH #ifdef HAVE_LAKKA + case MENU_ENUM_LABEL_CPU_PERF_MODE: + BIND_ACTION_RIGHT(cbs, cpu_policy_mode_change); + break; case MENU_ENUM_LABEL_CPU_POLICY_MAX_FREQ: case MENU_ENUM_LABEL_CPU_POLICY_MIN_FREQ: case MENU_ENUM_LABEL_CPU_POLICY_GOVERNOR: BIND_ACTION_RIGHT(cbs, cpu_policy_freq_tweak); break; + case MENU_ENUM_LABEL_CPU_MANAGED_MIN_FREQ: + case MENU_ENUM_LABEL_CPU_MANAGED_MAX_FREQ: + BIND_ACTION_RIGHT(cbs, cpu_policy_freq_managed_tweak); + break; + case MENU_ENUM_LABEL_CPU_POLICY_CORE_GOVERNOR: + case MENU_ENUM_LABEL_CPU_POLICY_MENU_GOVERNOR: + BIND_ACTION_RIGHT(cbs, cpu_policy_freq_managed_gov); + break; #endif #endif default: diff --git a/menu/cbs/menu_cbs_sublabel.c b/menu/cbs/menu_cbs_sublabel.c index 3c2851a4c1..00fbe1ec4f 100644 --- a/menu/cbs/menu_cbs_sublabel.c +++ b/menu/cbs/menu_cbs_sublabel.c @@ -1020,15 +1020,28 @@ static int action_bind_sublabel_cpu_policy_entry_list( { /* Displays info about the Policy entry */ cpu_scaling_driver_t **drivers = get_cpu_scaling_drivers(false); + int idx = atoi(path); if (drivers) { - sprintf(s, "%s | Freq: %u MHz\n", drivers[i]->scaling_governor, - drivers[i]->current_frequency / 1000); + sprintf(s, "%s | Freq: %u MHz\n", drivers[idx]->scaling_governor, + drivers[idx]->current_frequency / 1000); return 0; } return -1; } +static int action_bind_sublabel_cpu_perf_mode( + file_list_t *list, + unsigned type, unsigned i, + const char *label, const char *path, + char *s, size_t len) +{ + /* Displays info about the mode selected */ + enum cpu_scaling_mode mode = get_cpu_scaling_mode(NULL); + strlcpy(s, msg_hash_to_str( + MENU_ENUM_SUBLABEL_VALUE_CPU_PERF_MODE_MANAGED_PERF + (int)mode), len); + return 0; +} #endif #endif #ifdef HAVE_CHEEVOS @@ -3947,6 +3960,9 @@ int menu_cbs_init_bind_sublabel(menu_file_list_cbs_t *cbs, case MENU_ENUM_LABEL_CPU_POLICY_ENTRY: BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_cpu_policy_entry_list); break; + case MENU_ENUM_LABEL_CPU_PERF_MODE: + BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_cpu_perf_mode); + break; #endif #endif case MENU_ENUM_LABEL_USER_LANGUAGE: diff --git a/menu/menu_displaylist.c b/menu/menu_displaylist.c index 438a161d10..550d4ee99f 100644 --- a/menu/menu_displaylist.c +++ b/menu/menu_displaylist.c @@ -9869,17 +9869,66 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type, if (drivers) { int count = 0; - while (*drivers) + + menu_entries_append_enum(info->list, + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_CPU_PERF_MODE), + msg_hash_to_str(MENU_ENUM_LABEL_CPU_PERF_MODE), + MENU_ENUM_LABEL_CPU_PERF_MODE, + 0, 0, 0); + + switch (get_cpu_scaling_mode(NULL)) { - char policyid[16]; - sprintf(policyid, "%u", count++); + case CPUSCALING_MANUAL: + while (*drivers) + { + char policyid[16]; + sprintf(policyid, "%u", count++); + menu_entries_append_enum(info->list, + policyid, + policyid, + MENU_ENUM_LABEL_CPU_POLICY_ENTRY, + 0, 0, 0); + drivers++; + } + break; + case CPUSCALING_MANAGED_PER_CONTEXT: + /* Allows user to pick two governors */ menu_entries_append_enum(info->list, - policyid, - policyid, - MENU_ENUM_LABEL_CPU_POLICY_ENTRY, + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_CPU_POLICY_CORE_GOVERNOR), + "0", + MENU_ENUM_LABEL_CPU_POLICY_CORE_GOVERNOR, 0, 0, 0); - drivers++; - } + + menu_entries_append_enum(info->list, + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_CPU_POLICY_MENU_GOVERNOR), + "1", + MENU_ENUM_LABEL_CPU_POLICY_MENU_GOVERNOR, + 0, 0, 0); + + /* fallthrough */ + case CPUSCALING_MANAGED_PERFORMANCE: + /* Allow users to choose max/min frequencies */ + menu_entries_append_enum(info->list, + "0", + "0", + MENU_ENUM_LABEL_CPU_MANAGED_MIN_FREQ, + MENU_SETTINGS_CPU_MANAGED_SET_MINFREQ, + 0, 0); + + menu_entries_append_enum(info->list, + "1", + "1", + MENU_ENUM_LABEL_CPU_MANAGED_MAX_FREQ, + MENU_SETTINGS_CPU_MANAGED_SET_MAXFREQ, + 0, 0); + + break; + case CPUSCALING_MAX_PERFORMANCE: + case CPUSCALING_MIN_POWER: + case CPUSCALING_BALANCED: + /* No settings for these modes */ + break; + }; } info->need_push = true; diff --git a/menu/menu_driver.h b/menu/menu_driver.h index 19eb03fca3..1cff563d30 100644 --- a/menu/menu_driver.h +++ b/menu/menu_driver.h @@ -214,6 +214,8 @@ enum menu_settings_type MENU_SETTINGS_CPU_POLICY_SET_MINFREQ, MENU_SETTINGS_CPU_POLICY_SET_MAXFREQ, MENU_SETTINGS_CPU_POLICY_SET_GOVERNOR, + MENU_SETTINGS_CPU_MANAGED_SET_MINFREQ, + MENU_SETTINGS_CPU_MANAGED_SET_MAXFREQ, MENU_SET_CDROM_LIST, MENU_SET_LOAD_CDROM_LIST, diff --git a/misc/cpufreq/cpufreq.c b/misc/cpufreq/cpufreq.c index 1fba52ed97..84e8280b9f 100644 --- a/misc/cpufreq/cpufreq.c +++ b/misc/cpufreq/cpufreq.c @@ -24,12 +24,18 @@ #include <retro_miscellaneous.h> #include "cpufreq.h" +#include "../../configuration.h" #define REFRESH_TIMEOUT 2 #define CPU_POLICIES_DIR "/sys/devices/system/cpu/cpufreq/" static time_t last_update = 0; static cpu_scaling_driver_t **scaling_drivers = NULL; +/* Mode state and its options */ +static enum cpu_scaling_mode cur_smode = CPUSCALING_MANAGED_PERFORMANCE; +static cpu_scaling_opts_t cur_smode_opts = { 1, ~0U, "performance", "ondemand" }; +/* Precalculate and store the absolute max and min frequencies */ +static uint32_t abs_min_freq = 1, abs_max_freq = ~0U; static bool readparse_uint32(const char *path, uint32_t *value) { @@ -80,15 +86,6 @@ static void free_drivers(cpu_scaling_driver_t **d) } } -void cpu_scaling_driver_free() -{ - if (scaling_drivers) - free_drivers(scaling_drivers); - - scaling_drivers = NULL; - last_update = 0; -} - cpu_scaling_driver_t **get_cpu_scaling_drivers(bool can_update) { if (can_update && (time(NULL) > last_update + REFRESH_TIMEOUT || @@ -142,6 +139,12 @@ cpu_scaling_driver_t **get_cpu_scaling_drivers(bool can_update) "scaling_min_freq", sizeof(fpath)); readparse_uint32(fpath, &drv->min_policy_freq); + /* Check current freq limits and update them */ + if (abs_min_freq > drv->min_cpu_freq || abs_min_freq == 1) + abs_min_freq = drv->min_cpu_freq; + if (abs_max_freq < drv->max_cpu_freq || abs_max_freq == ~0U) + abs_max_freq = drv->max_cpu_freq; + fill_pathname_join(fpath, policy_dir->elems[i].data, "scaling_max_freq", sizeof(fpath)); readparse_uint32(fpath, &drv->max_policy_freq); @@ -160,6 +163,7 @@ cpu_scaling_driver_t **get_cpu_scaling_drivers(bool can_update) filestream_read_file(fpath, (void**)&drv->scaling_governor, NULL); string_remove_all_chars(drv->scaling_governor, '\n'); + /* This is not available in many platforms! */ fill_pathname_join(fpath, policy_dir->elems[i].data, "scaling_available_frequencies", sizeof(fpath)); tmplst = readparse_list(fpath); @@ -168,7 +172,12 @@ cpu_scaling_driver_t **get_cpu_scaling_drivers(bool can_update) drv->available_freqs = calloc(tmplst->size, sizeof(uint32_t)); for (j = 0; j < tmplst->size; j++) { - drv->available_freqs[j] = (uint32_t)atol(tmplst->elems[j].data); + uint32_t freq = (uint32_t)atol(tmplst->elems[j].data); + drv->available_freqs[j] = freq; + if (abs_min_freq > freq || abs_min_freq == 1) + abs_min_freq = freq; + if (abs_max_freq < freq || abs_max_freq == ~0U) + abs_max_freq = freq; } string_list_free(tmplst); } @@ -254,10 +263,30 @@ uint32_t get_cpu_scaling_next_frequency( freq = freq + step * 100000; } - if (freq > driver->max_cpu_freq) - freq = driver->max_cpu_freq; - if (freq < driver->min_cpu_freq) - freq = driver->min_cpu_freq; + freq = MIN(freq, driver->max_cpu_freq); + freq = MAX(freq, driver->min_cpu_freq); + + return freq; +} + +uint32_t get_cpu_scaling_next_frequency_limit(uint32_t freq, int step) +{ + /* Tune step, if it's smaller than 100MHz */ + unsigned fstep = 100000; + if ((abs_max_freq - abs_min_freq) / 20 < fstep) + fstep = 50000; + + if (freq <= abs_min_freq && step < 0) + return 1; /* Means "minimum frequency" */ + + if (freq >= abs_max_freq && step > 0) + return ~0U; /* Means "maximum frequency" */ + + /* Just do small steps towards the max/min */ + freq = freq + step * fstep; + + freq = MIN(freq, abs_max_freq); + freq = MAX(freq, abs_min_freq); return freq; } @@ -278,4 +307,136 @@ bool set_cpu_scaling_governor(cpu_scaling_driver_t *driver, const char* governor return false; } +static void steer_all_drivers( + const char *governor, + uint32_t minfreq, + uint32_t maxfreq) +{ + cpu_scaling_driver_t **drivers = get_cpu_scaling_drivers(false); + if (!drivers) + return; + while (*drivers) + { + cpu_scaling_driver_t *d = *drivers++; + if (minfreq) + set_cpu_scaling_min_frequency(d, MAX(minfreq, d->min_cpu_freq)); + if (maxfreq) + set_cpu_scaling_max_frequency(d, MIN(maxfreq, d->max_cpu_freq)); + set_cpu_scaling_governor(d, governor); + } +} + +void set_cpu_scaling_signal(enum cpu_scaling_event event) +{ + switch (cur_smode) { + case CPUSCALING_MANAGED_PERFORMANCE: + /* Bump to perf or fall back to ondemand depending on the RA state */ + if (event == CPUSCALING_EVENT_FOCUS_CORE) + steer_all_drivers("performance", cur_smode_opts.min_freq, + cur_smode_opts.max_freq); + else + steer_all_drivers("ondemand", 1, ~0U); + break; + case CPUSCALING_MANAGED_PER_CONTEXT: + /* Apply the right settings the user specified */ + if (event == CPUSCALING_EVENT_FOCUS_CORE) + steer_all_drivers(cur_smode_opts.main_policy, cur_smode_opts.min_freq, + cur_smode_opts.max_freq); + else + steer_all_drivers(cur_smode_opts.menu_policy, 1, ~0U); + break; + default: + break; + }; +} + +enum cpu_scaling_mode get_cpu_scaling_mode(cpu_scaling_opts_t *opts) +{ + if (opts) + *opts = cur_smode_opts; + return cur_smode; +} + +void set_cpu_scaling_mode( + enum cpu_scaling_mode mode, + const cpu_scaling_opts_t *opts) +{ + settings_t *settings = config_get_ptr(); + + /* Store current state */ + cur_smode = mode; + if (opts) + cur_smode_opts = *opts; + + switch (mode) + { + case CPUSCALING_MANUAL: + /* Do nothing, the UI allows for tweaking directly */ + break; + case CPUSCALING_MANAGED_PERFORMANCE: + case CPUSCALING_MANAGED_PER_CONTEXT: + /* Simulate a state change to enforce the policy */ + set_cpu_scaling_signal(CPUSCALING_EVENT_FOCUS_MENU); + break; + case CPUSCALING_MAX_PERFORMANCE: + // Set performance and bump frequencies to min/max + steer_all_drivers("performance", 1, ~0U); + break; + case CPUSCALING_MIN_POWER: + // Set powersave and bump frequencies to min/max + steer_all_drivers("powersave", 1, ~0U); + break; + case CPUSCALING_BALANCED: + // Set ondemand and bump frequencies to min/max + steer_all_drivers("ondemand", 1, ~0U); + break; + }; + + if (settings) + { + /* Store current settings */ + settings->uints.cpu_scaling_mode = (int)cur_smode; + settings->uints.cpu_min_freq = cur_smode_opts.min_freq; + settings->uints.cpu_max_freq = cur_smode_opts.max_freq; + + strlcpy(settings->arrays.cpu_main_gov, cur_smode_opts.main_policy, + sizeof(settings->arrays.cpu_main_gov)); + strlcpy(settings->arrays.cpu_menu_gov, cur_smode_opts.menu_policy, + sizeof(settings->arrays.cpu_menu_gov)); + } +}; + +void cpu_scaling_driver_free() +{ + if (scaling_drivers) + free_drivers(scaling_drivers); + + scaling_drivers = NULL; + last_update = 0; +} + +void cpu_scaling_driver_init(void) +{ + /* Read the default settings */ + settings_t *settings = config_get_ptr(); + unsigned mode = settings->uints.cpu_scaling_mode; + cur_smode_opts.min_freq = settings->uints.cpu_min_freq; + cur_smode_opts.max_freq = settings->uints.cpu_max_freq; + + if (mode <= (int)CPUSCALING_MANUAL) + cur_smode = (enum cpu_scaling_mode)mode; + + if (settings->arrays.cpu_main_gov[0]) + strlcpy(cur_smode_opts.main_policy, settings->arrays.cpu_main_gov, + sizeof(cur_smode_opts.main_policy)); + if (settings->arrays.cpu_menu_gov[0]) + strlcpy(cur_smode_opts.menu_policy, settings->arrays.cpu_menu_gov, + sizeof(cur_smode_opts.menu_policy)); + + /* Force update the policy tree */ + get_cpu_scaling_drivers(true); + + /* Force enforce these settings */ + set_cpu_scaling_mode(cur_smode, NULL); +} diff --git a/misc/cpufreq/cpufreq.h b/misc/cpufreq/cpufreq.h index 06712d4020..38607e6852 100644 --- a/misc/cpufreq/cpufreq.h +++ b/misc/cpufreq/cpufreq.h @@ -20,6 +20,36 @@ RETRO_BEGIN_DECLS +#define MAX_GOV_STRLEN 32 + +/* Events from Frontend to the driver to drive policies */ +enum cpu_scaling_event +{ + CPUSCALING_EVENT_FOCUS_CORE, + CPUSCALING_EVENT_FOCUS_MENU, + CPUSCALING_EVENT_FOCUS_SCREENSAVER +}; + +/* Scaling mode selected by the user */ +enum cpu_scaling_mode +{ + CPUSCALING_MANAGED_PERFORMANCE = 0, /* Performance while running core */ + CPUSCALING_MANAGED_PER_CONTEXT, /* Policies for core, menu, etc. */ + CPUSCALING_MAX_PERFORMANCE, /* Performance (Max Freq) */ + CPUSCALING_MIN_POWER, /* Use Powersave governor */ + CPUSCALING_BALANCED, /* Uses schedutil/ondemand */ + CPUSCALING_MANUAL, /* Can manually tweak stuff */ +}; + +typedef struct cpu_scaling_opts +{ + /* Max/Min frequencies */ + uint32_t min_freq, max_freq; + /* Options for CPUSCALING_POLICY_PER_CONTEXT */ + char main_policy[MAX_GOV_STRLEN]; + char menu_policy[MAX_GOV_STRLEN]; +} cpu_scaling_opts_t; + typedef struct cpu_scaling_driver { /* Policy number in the sysfs tree */ @@ -39,7 +69,10 @@ typedef struct cpu_scaling_driver } cpu_scaling_driver_t; /* Safely free all memory used by the driver */ -void cpu_scaling_driver_free(); +void cpu_scaling_driver_free(void); + +/* Signal the initialization */ +void cpu_scaling_driver_init(void); /* Get a list of the available cpu scaling drivers */ cpu_scaling_driver_t **get_cpu_scaling_drivers(bool can_update); @@ -53,10 +86,21 @@ bool set_cpu_scaling_max_frequency( /* Calculate next/previous frequencies */ uint32_t get_cpu_scaling_next_frequency(cpu_scaling_driver_t *driver, uint32_t freq, int step); +uint32_t get_cpu_scaling_next_frequency_limit(uint32_t freq, int step); /* Set the scaling governor for this scaling driver */ bool set_cpu_scaling_governor(cpu_scaling_driver_t *driver, const char* governor); +/* Signal certain events that are of interest of this driver */ +void set_cpu_scaling_signal(enum cpu_scaling_event); + +/* Set the base cpufreq policy mode */ +void set_cpu_scaling_mode(enum cpu_scaling_mode mode, + const cpu_scaling_opts_t *opts); + +/* Get the base cpufreq policy mode */ +enum cpu_scaling_mode get_cpu_scaling_mode(cpu_scaling_opts_t *opts); + RETRO_END_DECLS #endif diff --git a/msg_hash.h b/msg_hash.h index a0433b8cf5..64c9cbbd81 100644 --- a/msg_hash.h +++ b/msg_hash.h @@ -2941,11 +2941,30 @@ enum msg_hash_enums MENU_LABEL(MIDI_VOLUME), MENU_LABEL(SUSTAINED_PERFORMANCE_MODE), + MENU_LABEL(CPU_PERF_MODE), MENU_LABEL(CPU_PERFPOWER), MENU_LABEL(CPU_POLICY_ENTRY), MENU_LABEL(CPU_POLICY_MIN_FREQ), MENU_LABEL(CPU_POLICY_MAX_FREQ), MENU_LABEL(CPU_POLICY_GOVERNOR), + MENU_LABEL(CPU_POLICY_CORE_GOVERNOR), + MENU_LABEL(CPU_POLICY_MENU_GOVERNOR), + MENU_LABEL(CPU_MANAGED_MIN_FREQ), + MENU_LABEL(CPU_MANAGED_MAX_FREQ), + + MENU_ENUM_LABEL_VALUE_CPU_PERF_MODE_MANAGED_PERF, + MENU_ENUM_LABEL_VALUE_CPU_PERF_MODE_MANAGED_PER_CONTEXT, + MENU_ENUM_LABEL_VALUE_CPU_PERF_MODE_MAX_PERF, + MENU_ENUM_LABEL_VALUE_CPU_PERF_MODE_MIN_POWER, + MENU_ENUM_LABEL_VALUE_CPU_PERF_MODE_BALANCED, + MENU_ENUM_LABEL_VALUE_CPU_PERF_MODE_MANUAL, + + MENU_ENUM_SUBLABEL_VALUE_CPU_PERF_MODE_MANAGED_PERF, + MENU_ENUM_SUBLABEL_VALUE_CPU_PERF_MODE_MANAGED_PER_CONTEXT, + MENU_ENUM_SUBLABEL_VALUE_CPU_PERF_MODE_MAX_PERF, + MENU_ENUM_SUBLABEL_VALUE_CPU_PERF_MODE_MIN_POWER, + MENU_ENUM_SUBLABEL_VALUE_CPU_PERF_MODE_BALANCED, + MENU_ENUM_SUBLABEL_VALUE_CPU_PERF_MODE_MANUAL, MENU_ENUM_LABEL_CHEAT_HANDLER_TYPE_EMU, MENU_ENUM_LABEL_CHEAT_HANDLER_TYPE_RETRO, diff --git a/retroarch.c b/retroarch.c index b82cc1c540..ac12405922 100644 --- a/retroarch.c +++ b/retroarch.c @@ -228,6 +228,7 @@ #include "gfx/video_crt_switch.h" #include "bluetooth/bluetooth_driver.h" #include "wifi/wifi_driver.h" +#include "misc/cpufreq/cpufreq.h" #include "led/led_driver.h" #include "midi/midi_driver.h" #include "core.h" @@ -13314,10 +13315,18 @@ static void retroarch_pause_checks(struct rarch_state *p_rarch) userdata.status = DISCORD_PRESENCE_GAME_PAUSED; command_event(CMD_EVENT_DISCORD_UPDATE, &userdata); #endif + +#ifdef HAVE_LAKKA + set_cpu_scaling_signal(CPUSCALING_EVENT_FOCUS_MENU); +#endif } else { RARCH_LOG("[Core]: %s\n", msg_hash_to_str(MSG_UNPAUSED)); + +#ifdef HAVE_LAKKA + set_cpu_scaling_signal(CPUSCALING_EVENT_FOCUS_CORE); +#endif } #if defined(HAVE_TRANSLATE) && defined(HAVE_GFX_WIDGETS) @@ -33206,6 +33215,10 @@ static void drivers_init(struct rarch_state *p_rarch, /* Initialize MIDI driver */ if (flags & DRIVER_MIDI_MASK) midi_driver_init(p_rarch, settings); + +#ifdef HAVE_LAKKA + cpu_scaling_driver_init(); +#endif } /** @@ -33298,6 +33311,10 @@ static void driver_uninit(struct rarch_state *p_rarch, int flags) if (flags & DRIVER_MIDI_MASK) midi_driver_free(p_rarch); + +#ifdef HAVE_LAKKA + cpu_scaling_driver_free(); +#endif } static void retroarch_deinit_drivers( @@ -35632,6 +35649,13 @@ static void menu_driver_toggle( p_rarch->menu_driver_alive = on; +#ifdef HAVE_LAKKA + if (on) + set_cpu_scaling_signal(CPUSCALING_EVENT_FOCUS_MENU); + else + set_cpu_scaling_signal(CPUSCALING_EVENT_FOCUS_CORE); +#endif + /* Apply any required menu pointer input inhibits * (i.e. prevent phantom input when using an overlay * to toggle the menu on) */