MALI: utgard: upgrade DDK to r6p1-01rel0
[firefly-linux-kernel-4.4.55.git] / drivers / gpu / arm / mali400 / mali / common / mali_dvfs_policy.c
1 /*
2  * Copyright (C) 2010-2012, 2014, 2016 ARM Limited. All rights reserved.
3  * 
4  * This program is free software and is provided to you under the terms of the GNU General Public License version 2
5  * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence.
6  * 
7  * A copy of the licence is included with the program, and can also be obtained from Free Software
8  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
9  */
10
11 #include <linux/mali/mali_utgard.h>
12 #include "mali_kernel_common.h"
13 #include "mali_scheduler.h"
14 #include "mali_dvfs_policy.h"
15 #include "mali_osk_mali.h"
16 #include "mali_osk_profiling.h"
17
18 #define CLOCK_TUNING_TIME_DEBUG 0
19
20 #define MAX_PERFORMANCE_VALUE 256
21 #define MALI_PERCENTAGE_TO_UTILIZATION_FRACTION(percent) ((int) ((percent)*(MAX_PERFORMANCE_VALUE)/100.0 + 0.5))
22
23 /** The max fps the same as display vsync default 60, can set by module insert parameter */
24 int mali_max_system_fps = 60;
25 /** A lower limit on their desired FPS default 58, can set by module insert parameter */
26 int mali_desired_fps = 58;
27
28 static int mali_fps_step1 = 0;
29 static int mali_fps_step2 = 0;
30
31 static int clock_step = -1;
32 static int cur_clk_step = -1;
33 static struct mali_gpu_clock *gpu_clk = NULL;
34
35 /*Function prototype */
36 static int (*mali_gpu_set_freq)(int) = NULL;
37 static int (*mali_gpu_get_freq)(void) = NULL;
38
39 static mali_bool mali_dvfs_enabled = MALI_FALSE;
40
41 #define NUMBER_OF_NANOSECONDS_PER_SECOND  1000000000ULL
42 static u32 calculate_window_render_fps(u64 time_period)
43 {
44         u32 max_window_number;
45         u64 tmp;
46         u64 max = time_period;
47         u32 leading_zeroes;
48         u32 shift_val;
49         u32 time_period_shift;
50         u32 max_window_number_shift;
51         u32 ret_val;
52
53         max_window_number = mali_session_max_window_num();
54
55         /* To avoid float division, extend the dividend to ns unit */
56         tmp = (u64)max_window_number * NUMBER_OF_NANOSECONDS_PER_SECOND;
57         if (tmp > time_period) {
58                 max = tmp;
59         }
60
61         /*
62          * We may have 64-bit values, a dividend or a divisor or both
63          * To avoid dependencies to a 64-bit divider, we shift down the two values
64          * equally first.
65          */
66         leading_zeroes = _mali_osk_clz((u32)(max >> 32));
67         shift_val = 32 - leading_zeroes;
68
69         time_period_shift = (u32)(time_period >> shift_val);
70         max_window_number_shift = (u32)(tmp >> shift_val);
71
72         ret_val = max_window_number_shift / time_period_shift;
73
74         return ret_val;
75 }
76
77 static bool mali_pickup_closest_avail_clock(int target_clock_mhz, mali_bool pick_clock_up)
78 {
79         int i = 0;
80         bool clock_changed = false;
81
82         /* Round up the closest available frequency step for target_clock_hz */
83         for (i = 0; i < gpu_clk->num_of_steps; i++) {
84                 /* Find the first item > target_clock_hz */
85                 if (((int)(gpu_clk->item[i].clock) - target_clock_mhz) > 0) {
86                         break;
87                 }
88         }
89
90         /* If the target clock greater than the maximum clock just pick the maximum one*/
91         if (i == gpu_clk->num_of_steps) {
92                 i = gpu_clk->num_of_steps - 1;
93         } else {
94                 if ((!pick_clock_up) && (i > 0)) {
95                         i = i - 1;
96                 }
97         }
98
99         clock_step = i;
100         if (cur_clk_step != clock_step) {
101                 clock_changed = true;
102         }
103
104         return clock_changed;
105 }
106
107 void mali_dvfs_policy_realize(struct mali_gpu_utilization_data *data, u64 time_period)
108 {
109         int under_perform_boundary_value = 0;
110         int over_perform_boundary_value = 0;
111         int current_fps = 0;
112         int current_gpu_util = 0;
113         bool clock_changed = false;
114 #if CLOCK_TUNING_TIME_DEBUG
115         struct timeval start;
116         struct timeval stop;
117         unsigned int elapse_time;
118         do_gettimeofday(&start);
119 #endif
120         u32 window_render_fps;
121
122         if (NULL == gpu_clk) {
123                 MALI_DEBUG_PRINT(2, ("Enable DVFS but patform doesn't Support freq change. \n"));
124                 return;
125         }
126
127         window_render_fps = calculate_window_render_fps(time_period);
128
129         current_fps = window_render_fps;
130         current_gpu_util = data->utilization_gpu;
131
132         /* Get the specific under_perform_boundary_value and over_perform_boundary_value */
133         if ((mali_desired_fps <= current_fps) && (current_fps < mali_max_system_fps)) {
134                 under_perform_boundary_value = MALI_PERCENTAGE_TO_UTILIZATION_FRACTION(90);
135                 over_perform_boundary_value = MALI_PERCENTAGE_TO_UTILIZATION_FRACTION(70);
136         } else if ((mali_fps_step1 <= current_fps) && (current_fps < mali_desired_fps)) {
137                 under_perform_boundary_value = MALI_PERCENTAGE_TO_UTILIZATION_FRACTION(55);
138                 over_perform_boundary_value = MALI_PERCENTAGE_TO_UTILIZATION_FRACTION(35);
139         } else if ((mali_fps_step2 <= current_fps) && (current_fps < mali_fps_step1)) {
140                 under_perform_boundary_value = MALI_PERCENTAGE_TO_UTILIZATION_FRACTION(70);
141                 over_perform_boundary_value = MALI_PERCENTAGE_TO_UTILIZATION_FRACTION(50);
142         } else {
143                 under_perform_boundary_value = MALI_PERCENTAGE_TO_UTILIZATION_FRACTION(55);
144                 over_perform_boundary_value = MALI_PERCENTAGE_TO_UTILIZATION_FRACTION(35);
145         }
146
147         MALI_DEBUG_PRINT(5, ("Using ARM power policy: gpu util = %d \n", current_gpu_util));
148         MALI_DEBUG_PRINT(5, ("Using ARM power policy: under_perform = %d,  over_perform = %d \n", under_perform_boundary_value, over_perform_boundary_value));
149         MALI_DEBUG_PRINT(5, ("Using ARM power policy: render fps = %d,  pressure render fps = %d \n", current_fps, window_render_fps));
150
151         /* Get current clock value */
152         cur_clk_step = mali_gpu_get_freq();
153
154         /* Consider offscreen */
155         if (0 == current_fps) {
156                 /* GP or PP under perform, need to give full power */
157                 if (current_gpu_util > over_perform_boundary_value) {
158                         if (cur_clk_step != gpu_clk->num_of_steps - 1) {
159                                 clock_changed = true;
160                                 clock_step = gpu_clk->num_of_steps - 1;
161                         }
162                 }
163
164                 /* If GPU is idle, use lowest power */
165                 if (0 == current_gpu_util) {
166                         if (cur_clk_step != 0) {
167                                 clock_changed = true;
168                                 clock_step = 0;
169                         }
170                 }
171
172                 goto real_setting;
173         }
174
175         /* 2. Calculate target clock if the GPU clock can be tuned */
176         if (-1 != cur_clk_step) {
177                 int target_clk_mhz = -1;
178                 mali_bool pick_clock_up = MALI_TRUE;
179
180                 if (current_gpu_util > under_perform_boundary_value) {
181                         /* when under perform, need to consider the fps part */
182                         target_clk_mhz = gpu_clk->item[cur_clk_step].clock * current_gpu_util * mali_desired_fps / under_perform_boundary_value / current_fps;
183                         pick_clock_up = MALI_TRUE;
184                 } else if (current_gpu_util < over_perform_boundary_value) {
185                         /* when over perform, did't need to consider fps, system didn't want to reach desired fps */
186                         target_clk_mhz = gpu_clk->item[cur_clk_step].clock * current_gpu_util / under_perform_boundary_value;
187                         pick_clock_up = MALI_FALSE;
188                 }
189
190                 if (-1 != target_clk_mhz) {
191                         clock_changed = mali_pickup_closest_avail_clock(target_clk_mhz, pick_clock_up);
192                 }
193         }
194
195 real_setting:
196         if (clock_changed) {
197                 mali_gpu_set_freq(clock_step);
198
199                 _mali_osk_profiling_add_event(MALI_PROFILING_EVENT_TYPE_SINGLE |
200                                               MALI_PROFILING_EVENT_CHANNEL_GPU |
201                                               MALI_PROFILING_EVENT_REASON_SINGLE_GPU_FREQ_VOLT_CHANGE,
202                                               gpu_clk->item[clock_step].clock,
203                                               gpu_clk->item[clock_step].vol / 1000,
204                                               0, 0, 0);
205         }
206
207 #if CLOCK_TUNING_TIME_DEBUG
208         do_gettimeofday(&stop);
209
210         elapse_time = timeval_to_ns(&stop) - timeval_to_ns(&start);
211         MALI_DEBUG_PRINT(2, ("Using ARM power policy:  eclapse time = %d\n", elapse_time));
212 #endif
213 }
214
215 _mali_osk_errcode_t mali_dvfs_policy_init(void)
216 {
217         _mali_osk_device_data data;
218         _mali_osk_errcode_t err = _MALI_OSK_ERR_OK;
219
220         if (_MALI_OSK_ERR_OK == _mali_osk_device_data_get(&data)) {
221                 if ((NULL != data.get_clock_info) && (NULL != data.set_freq) && (NULL != data.get_freq)) {
222                         MALI_DEBUG_PRINT(2, ("Mali DVFS init: using arm dvfs policy \n"));
223
224
225                         mali_fps_step1 = mali_max_system_fps / 3;
226                         mali_fps_step2 = mali_max_system_fps / 5;
227
228                         data.get_clock_info(&gpu_clk);
229
230                         if (gpu_clk != NULL) {
231 #ifdef DEBUG
232                                 int i;
233                                 for (i = 0; i < gpu_clk->num_of_steps; i++) {
234                                         MALI_DEBUG_PRINT(5, ("mali gpu clock info: step%d clock(%d)Hz,vol(%d) \n",
235                                                              i, gpu_clk->item[i].clock, gpu_clk->item[i].vol));
236                                 }
237 #endif
238                         } else {
239                                 MALI_DEBUG_PRINT(2, ("Mali DVFS init: platform didn't define enough info for ddk to do DVFS \n"));
240                         }
241
242                         mali_gpu_get_freq = data.get_freq;
243                         mali_gpu_set_freq = data.set_freq;
244
245                         if ((NULL != gpu_clk) && (gpu_clk->num_of_steps > 0)
246                             && (NULL != mali_gpu_get_freq) && (NULL != mali_gpu_set_freq)) {
247                                 mali_dvfs_enabled = MALI_TRUE;
248                         }
249                 } else {
250                         MALI_DEBUG_PRINT(2, ("Mali DVFS init: platform function callback incomplete, need check mali_gpu_device_data in platform .\n"));
251                 }
252         } else {
253                 err = _MALI_OSK_ERR_FAULT;
254                 MALI_DEBUG_PRINT(2, ("Mali DVFS init: get platform data error .\n"));
255         }
256
257         return err;
258 }
259
260 /*
261  * Always give full power when start a new period,
262  * if mali dvfs enabled, for performance consideration
263  */
264 void mali_dvfs_policy_new_period(void)
265 {
266         /* Always give full power when start a new period */
267         unsigned int cur_clk_step = 0;
268
269         cur_clk_step = mali_gpu_get_freq();
270
271         if (cur_clk_step != (gpu_clk->num_of_steps - 1)) {
272                 mali_gpu_set_freq(gpu_clk->num_of_steps - 1);
273
274                 _mali_osk_profiling_add_event(MALI_PROFILING_EVENT_TYPE_SINGLE |
275                                               MALI_PROFILING_EVENT_CHANNEL_GPU |
276                                               MALI_PROFILING_EVENT_REASON_SINGLE_GPU_FREQ_VOLT_CHANGE, gpu_clk->item[gpu_clk->num_of_steps - 1].clock,
277                                               gpu_clk->item[gpu_clk->num_of_steps - 1].vol / 1000, 0, 0, 0);
278         }
279 }
280
281 mali_bool mali_dvfs_policy_enabled(void)
282 {
283         return mali_dvfs_enabled;
284 }
285
286 #if defined(CONFIG_MALI400_PROFILING)
287 void mali_get_current_gpu_clk_item(struct mali_gpu_clk_item *clk_item)
288 {
289         if (mali_platform_device != NULL) {
290
291                 struct mali_gpu_device_data *device_data = NULL;
292                 device_data = (struct mali_gpu_device_data *)mali_platform_device->dev.platform_data;
293
294                 if ((NULL != device_data->get_clock_info) && (NULL != device_data->get_freq)) {
295
296                         int cur_clk_step = device_data->get_freq();
297                         struct mali_gpu_clock *mali_gpu_clk = NULL;
298
299                         device_data->get_clock_info(&mali_gpu_clk);
300                         clk_item->clock = mali_gpu_clk->item[cur_clk_step].clock;
301                         clk_item->vol = mali_gpu_clk->item[cur_clk_step].vol;
302                 } else {
303                         MALI_DEBUG_PRINT(2, ("Mali GPU Utilization: platform function callback incomplete, need check mali_gpu_device_data in platform .\n"));
304                 }
305         }
306 }
307 #endif
308