Zenith CPUFreq Governor An adaptive, profile-driven CPU frequency governor for Android 12 GKI 5.10 kernels.
Zenith replaces the stock schedutil / ondemand governor with a multi-tier decision engine. It reads the same signals the scheduler does β CPU utilization, iowait, input activity, thermal pressure, PSI β and layers scenario awareness (rendering, audio, camera) and workload classification on top. The goal: lower interactive latency when you're using the phone, lower idle drain when you're not, and stay out of the way the rest of the time.
Requirements
Android kernel common-android12-5.10 (AOSP GKI branch)
arm64 (the only arch tested; portable in principle)
Clang 12.x or newer, matching the AOSP GKI toolchain
Cy, Cy in your defconfig
No vendor-module ABI changes β single-file drop into kernel/sched/cpufreq_zenith.c, no header changes, no new KMI symbols
How it picks a frequency
Every sample, Zenith walks a ladder of decision tiers β the highest tier that matches wins:
Input boost β touch event β temporary floor, decays linearly or cubically
Brutality snap β sustained heavy load β policy->max instantly (streak-gated to avoid spikes)
Hispeed β medium-high load β jump to a hispeed band
Frame-pace floor β if render thread is active β at least enough freq to hit the vblank budget
Scenario floors β audio / render / camera threads detected β per-tier floor
EAS / EM-based pick β default tier, uses the energy model
Efficient-freq ladder β low load β cap at one of the configured efficient bins
Light-load cap β very low load β drop to policy->min
On top of that, PSI pressure (memory / CPU / IO), uclamp min/max, and thermal derate act as multiplicative caps.
Profiles
Five curated presets that snapshot ~17 tunables each:
performance β aggressive ramp, no rate limits, full pin on touch
balanced β defaults + gentle bias (same shape as a stock Zenith install)
battery β slow ramp, big down-bias, capped boost, skip hispeed entirely
legacy β cpufreq_ondemand look-alike (no hysteresis)
custom β no preset, operator wires the tunables by hand
Apply a profile from:
cmdline at boot: zenith.profile=performance or per-policy zenith.policy_profile=0:battery,4:performance
sysfs at runtime: echo battery > /sys/.../cpufreq/policy*/zenith/profile
auto-tune (auto_tune=1): classifier runs every 10 s and picks a profile from load saturation + input-event rate, with optional scenario overlay (camera/render β performance, memstall β battery, audio β balanced)
Feature highlights
Input-aware boost with linear or cubic decay, cap %, and duration knobs
Frame-pace floor tied to a configurable vblank budget (16.7 ms / 8.3 ms / 6.9 ms)
Render / audio / camera awareness via comm-name matching (sysfs-configurable at runtime)
PSI-aware caps on memory pressure (CPU and IO dimensions too)
Thermal derate β level-based and rate-of-change
Efficient-freq ladder β up to 8 bins with per-bin hysteresis
uclamp floor/ceiling β respects per-task sched uclamp
Game mode levels 0/1/2 β composable overlay for aggressive scenarios
EAS fallthrough β when no tier fires, hands off to the EM-driven picker
Auto-tune classifier β periodic workload shape classification with scenario overrides
Decision stats β sysfs node dumps per-tier hit counts, boost activations, ladder holds, predictor accuracy
Zenith_decision tracepoint β single enriched tracepoint per frequency decision for perfetto / ftrace
Tuning
Every feature above is backed by a sysfs knob under /sys/devices/system/cpu/cpufreq/policy*/zenith/. New tunables default to their legacy value, so a fresh install behaves identically to a stock Zenith build until you opt in. profile_values (read-only) dumps every profile's tunable values if you want to see what each preset does without source-grepping.
Credits : https://t.me/ShadowbytePrjkt/3132