diff options
Diffstat (limited to 'omap5/sgx_src/eurasia_km/services4/system/omap4/sgxfreq_on3demand.c')
-rw-r--r-- | omap5/sgx_src/eurasia_km/services4/system/omap4/sgxfreq_on3demand.c | 324 |
1 files changed, 0 insertions, 324 deletions
diff --git a/omap5/sgx_src/eurasia_km/services4/system/omap4/sgxfreq_on3demand.c b/omap5/sgx_src/eurasia_km/services4/system/omap4/sgxfreq_on3demand.c deleted file mode 100644 index c4e4bd9..0000000 --- a/omap5/sgx_src/eurasia_km/services4/system/omap4/sgxfreq_on3demand.c +++ /dev/null | |||
@@ -1,324 +0,0 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2012 Texas Instruments, Inc | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify it | ||
5 | * under the terms of the GNU General Public License version 2 as published by | ||
6 | * the Free Software Foundation. | ||
7 | * | ||
8 | * This program is distributed in the hope that it will be useful, but WITHOUT | ||
9 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
10 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
11 | * more details. | ||
12 | * | ||
13 | * You should have received a copy of the GNU General Public License along with | ||
14 | * this program. If not, see <http://www.gnu.org/licenses/>. | ||
15 | */ | ||
16 | |||
17 | #include <linux/sysfs.h> | ||
18 | #include <linux/vmalloc.h> | ||
19 | #include <asm/io.h> | ||
20 | #include "sgxfreq.h" | ||
21 | |||
22 | static int on3demand_start(struct sgxfreq_sgx_data *data); | ||
23 | static void on3demand_stop(void); | ||
24 | static void on3demand_predict(void); | ||
25 | static void on3demand_frame_done(void); | ||
26 | static void on3demand_active(void); | ||
27 | static void on3demand_timeout(struct work_struct *work); | ||
28 | |||
29 | |||
30 | static struct sgxfreq_governor on3demand_gov = { | ||
31 | .name = "on3demand", | ||
32 | .gov_start = on3demand_start, | ||
33 | .gov_stop = on3demand_stop, | ||
34 | .sgx_frame_done = on3demand_frame_done, | ||
35 | .sgx_active = on3demand_active, | ||
36 | }; | ||
37 | |||
38 | static struct on3demand_data { | ||
39 | unsigned int load; | ||
40 | unsigned int up_threshold; | ||
41 | unsigned int down_threshold; | ||
42 | unsigned int history_size; | ||
43 | unsigned long prev_total_idle; | ||
44 | unsigned long prev_total_active; | ||
45 | unsigned int low_load_cnt; | ||
46 | unsigned int poll_interval; | ||
47 | unsigned long delta_active; | ||
48 | unsigned long delta_idle; | ||
49 | bool polling_enabled; | ||
50 | struct delayed_work work; | ||
51 | struct mutex mutex; | ||
52 | } odd; | ||
53 | |||
54 | #define ON3DEMAND_DEFAULT_UP_THRESHOLD 80 | ||
55 | #define ON3DEMAND_DEFAULT_DOWN_THRESHOLD 30 | ||
56 | #define ON3DEMAND_DEFAULT_HISTORY_SIZE_THRESHOLD 5 | ||
57 | /* For Live wallpaper frame done at interval of ~64ms */ | ||
58 | #define ON3DEMAND_DEFAULT_POLL_INTERVAL 75 | ||
59 | |||
60 | /*FIXME: This should be dynamic and queried from platform */ | ||
61 | #define ON3DEMAND_FRAME_DONE_DEADLINE_MS 16 | ||
62 | |||
63 | |||
64 | /*********************** begin sysfs interface ***********************/ | ||
65 | |||
66 | extern struct kobject *sgxfreq_kobj; | ||
67 | |||
68 | static ssize_t show_down_threshold(struct device *dev, | ||
69 | struct device_attribute *attr, char *buf) | ||
70 | { | ||
71 | return sprintf(buf, "%u\n", odd.down_threshold); | ||
72 | } | ||
73 | |||
74 | static ssize_t store_down_threshold(struct device *dev, | ||
75 | struct device_attribute *attr, const char *buf, size_t count) | ||
76 | { | ||
77 | int ret; | ||
78 | unsigned int thres; | ||
79 | |||
80 | ret = sscanf(buf, "%u", &thres); | ||
81 | if (ret != 1) | ||
82 | return -EINVAL; | ||
83 | |||
84 | mutex_lock(&odd.mutex); | ||
85 | |||
86 | if (thres <= 100) { | ||
87 | odd.down_threshold = thres; | ||
88 | odd.low_load_cnt = 0; | ||
89 | } else { | ||
90 | return -EINVAL; | ||
91 | } | ||
92 | |||
93 | mutex_unlock(&odd.mutex); | ||
94 | |||
95 | return count; | ||
96 | } | ||
97 | |||
98 | static ssize_t show_up_threshold(struct device *dev, | ||
99 | struct device_attribute *attr, char *buf) | ||
100 | { | ||
101 | return sprintf(buf, "%u\n", odd.up_threshold); | ||
102 | } | ||
103 | |||
104 | static ssize_t store_up_threshold(struct device *dev, | ||
105 | struct device_attribute *attr, const char *buf, size_t count) | ||
106 | { | ||
107 | int ret; | ||
108 | unsigned int thres; | ||
109 | |||
110 | ret = sscanf(buf, "%u", &thres); | ||
111 | if (ret != 1) | ||
112 | return -EINVAL; | ||
113 | |||
114 | mutex_lock(&odd.mutex); | ||
115 | |||
116 | if (thres <= 100) { | ||
117 | odd.up_threshold = thres; | ||
118 | odd.low_load_cnt = 0; | ||
119 | } else { | ||
120 | return -EINVAL; | ||
121 | } | ||
122 | |||
123 | mutex_unlock(&odd.mutex); | ||
124 | |||
125 | return count; | ||
126 | } | ||
127 | |||
128 | static ssize_t show_history_size(struct device *dev, | ||
129 | struct device_attribute *attr, char *buf) | ||
130 | { | ||
131 | return sprintf(buf, "%u\n", odd.history_size); | ||
132 | } | ||
133 | |||
134 | static ssize_t store_history_size(struct device *dev, | ||
135 | struct device_attribute *attr, const char *buf, size_t count) | ||
136 | { | ||
137 | int ret; | ||
138 | unsigned int size; | ||
139 | |||
140 | ret = sscanf(buf, "%u", &size); | ||
141 | if (ret != 1) | ||
142 | return -EINVAL; | ||
143 | |||
144 | mutex_lock(&odd.mutex); | ||
145 | |||
146 | if (size >= 1) { | ||
147 | odd.history_size = size; | ||
148 | odd.low_load_cnt = 0; | ||
149 | } else { | ||
150 | return -EINVAL; | ||
151 | } | ||
152 | |||
153 | mutex_unlock(&odd.mutex); | ||
154 | |||
155 | return count; | ||
156 | } | ||
157 | |||
158 | static ssize_t show_load(struct device *dev, | ||
159 | struct device_attribute *attr, char *buf) | ||
160 | { | ||
161 | return sprintf(buf, "%u\n", odd.load); | ||
162 | } | ||
163 | |||
164 | static DEVICE_ATTR(down_threshold, 0644, | ||
165 | show_down_threshold, store_down_threshold); | ||
166 | static DEVICE_ATTR(up_threshold, 0644, | ||
167 | show_up_threshold, store_up_threshold); | ||
168 | static DEVICE_ATTR(history_size, 0644, | ||
169 | show_history_size, store_history_size); | ||
170 | static DEVICE_ATTR(load, 0444, | ||
171 | show_load, NULL); | ||
172 | |||
173 | static struct attribute *on3demand_attributes[] = { | ||
174 | &dev_attr_down_threshold.attr, | ||
175 | &dev_attr_up_threshold.attr, | ||
176 | &dev_attr_history_size.attr, | ||
177 | &dev_attr_load.attr, | ||
178 | NULL | ||
179 | }; | ||
180 | |||
181 | static struct attribute_group on3demand_attr_group = { | ||
182 | .attrs = on3demand_attributes, | ||
183 | .name = "on3demand", | ||
184 | }; | ||
185 | /************************ end sysfs interface ************************/ | ||
186 | |||
187 | int on3demand_init(void) | ||
188 | { | ||
189 | int ret; | ||
190 | |||
191 | mutex_init(&odd.mutex); | ||
192 | |||
193 | ret = sgxfreq_register_governor(&on3demand_gov); | ||
194 | if (ret) | ||
195 | return ret; | ||
196 | |||
197 | return 0; | ||
198 | } | ||
199 | |||
200 | int on3demand_deinit(void) | ||
201 | { | ||
202 | return 0; | ||
203 | } | ||
204 | |||
205 | static int on3demand_start(struct sgxfreq_sgx_data *data) | ||
206 | { | ||
207 | int ret; | ||
208 | |||
209 | odd.load = 0; | ||
210 | odd.up_threshold = ON3DEMAND_DEFAULT_UP_THRESHOLD; | ||
211 | odd.down_threshold = ON3DEMAND_DEFAULT_DOWN_THRESHOLD; | ||
212 | odd.history_size = ON3DEMAND_DEFAULT_HISTORY_SIZE_THRESHOLD; | ||
213 | odd.prev_total_active = 0; | ||
214 | odd.prev_total_idle = 0; | ||
215 | odd.low_load_cnt = 0; | ||
216 | odd.poll_interval = ON3DEMAND_DEFAULT_POLL_INTERVAL; | ||
217 | odd.polling_enabled = false; | ||
218 | |||
219 | INIT_DELAYED_WORK(&odd.work, on3demand_timeout); | ||
220 | |||
221 | ret = sysfs_create_group(sgxfreq_kobj, &on3demand_attr_group); | ||
222 | if (ret) | ||
223 | return ret; | ||
224 | |||
225 | return 0; | ||
226 | } | ||
227 | |||
228 | static void on3demand_stop(void) | ||
229 | { | ||
230 | cancel_delayed_work_sync(&odd.work); | ||
231 | sysfs_remove_group(sgxfreq_kobj, &on3demand_attr_group); | ||
232 | } | ||
233 | |||
234 | static void on3demand_predict(void) | ||
235 | { | ||
236 | static unsigned short first_sample = 1; | ||
237 | unsigned long total_active, total_idle; | ||
238 | unsigned long freq; | ||
239 | |||
240 | if (first_sample == 1) { | ||
241 | first_sample = 0; | ||
242 | odd.prev_total_active = sgxfreq_get_total_active_time(); | ||
243 | odd.prev_total_idle = sgxfreq_get_total_idle_time(); | ||
244 | return; | ||
245 | } | ||
246 | |||
247 | /* Sample new active and idle times */ | ||
248 | total_active = sgxfreq_get_total_active_time(); | ||
249 | total_idle = sgxfreq_get_total_idle_time(); | ||
250 | |||
251 | /* Compute load */ | ||
252 | odd.delta_active = __delta32(total_active, odd.prev_total_active); | ||
253 | odd.delta_idle = __delta32(total_idle, odd.prev_total_idle); | ||
254 | |||
255 | /* | ||
256 | * If SGX was active for longer than frame display time (1/fps), | ||
257 | * scale to highest possible frequency. | ||
258 | */ | ||
259 | if (odd.delta_active > ON3DEMAND_FRAME_DONE_DEADLINE_MS) { | ||
260 | odd.low_load_cnt = 0; | ||
261 | sgxfreq_set_freq_request(sgxfreq_get_freq_max()); | ||
262 | } | ||
263 | |||
264 | if ((odd.delta_active + odd.delta_idle)) | ||
265 | odd.load = (100 * odd.delta_active / (odd.delta_active + odd.delta_idle)); | ||
266 | |||
267 | odd.prev_total_active = total_active; | ||
268 | odd.prev_total_idle = total_idle; | ||
269 | |||
270 | /* Scale GPU frequency on purpose */ | ||
271 | if (odd.load >= odd.up_threshold) { | ||
272 | odd.low_load_cnt = 0; | ||
273 | sgxfreq_set_freq_request(sgxfreq_get_freq_max()); | ||
274 | } else if (odd.load <= odd.down_threshold) { | ||
275 | if (odd.low_load_cnt == odd.history_size) { | ||
276 | /* Convert load to frequency */ | ||
277 | freq = (sgxfreq_get_freq() * odd.load) / 100; | ||
278 | sgxfreq_set_freq_request(freq); | ||
279 | odd.low_load_cnt = 0; | ||
280 | } else { | ||
281 | odd.low_load_cnt++; | ||
282 | } | ||
283 | } else { | ||
284 | odd.low_load_cnt = 0; | ||
285 | } | ||
286 | } | ||
287 | |||
288 | |||
289 | static void on3demand_active(void) | ||
290 | { | ||
291 | if (!odd.polling_enabled) { | ||
292 | sgxfreq_set_freq_request(sgxfreq_get_freq_max()); | ||
293 | odd.low_load_cnt = 0; | ||
294 | odd.polling_enabled = true; | ||
295 | schedule_delayed_work(&odd.work, odd.poll_interval * HZ/1000); | ||
296 | } | ||
297 | |||
298 | } | ||
299 | |||
300 | static void on3demand_frame_done(void) | ||
301 | { | ||
302 | if (odd.polling_enabled) { | ||
303 | cancel_delayed_work_sync(&odd.work); | ||
304 | schedule_delayed_work(&odd.work, odd.poll_interval * HZ/1000); | ||
305 | } | ||
306 | on3demand_predict(); | ||
307 | } | ||
308 | |||
309 | static void on3demand_timeout(struct work_struct *work) | ||
310 | { | ||
311 | /* | ||
312 | * If sgx was idle all throughout timer disable polling and | ||
313 | * enable it on next sgx active event | ||
314 | */ | ||
315 | if (!odd.delta_active) { | ||
316 | sgxfreq_set_freq_request(sgxfreq_get_freq_min()); | ||
317 | odd.low_load_cnt = 0; | ||
318 | odd.polling_enabled = false; | ||
319 | } else { | ||
320 | on3demand_predict(); | ||
321 | odd.polling_enabled = true; | ||
322 | schedule_delayed_work(&odd.work, odd.poll_interval * HZ/1000); | ||
323 | } | ||
324 | } | ||