fix mali for ttab
[GitHub/mt8127/android_kernel_alcatel_ttab.git] / drivers / misc / mediatek / gpu / ged / src / ged_notify_sw_vsync.c
CommitLineData
db9a41fa
S
1#include <linux/version.h>
2#include <linux/workqueue.h>
3#include <linux/sched.h>
4#include <asm/atomic.h>
5
6#include <linux/kernel.h>
7#include <linux/hrtimer.h>
8#include <linux/ktime.h>
9
10#include <asm/div64.h>
11
765691f5 12#include <linux/mtk_gpu_utility.h>
db9a41fa
S
13#include "ged_notify_sw_vsync.h"
14#include "ged_log.h"
15#include "ged_base.h"
16#include "ged_monitor_3D_fence.h"
17
18#define GED_DVFS_TIMER_TIMEOUT 25000000
19
20static struct hrtimer g_HT_hwvsync_emu;
21
22#include "ged_dvfs.h"
23
24extern void (*mtk_gpu_sodi_entry_fp)(void);
25extern void (*mtk_gpu_sodi_exit_fp)(void);
26
27
28static struct workqueue_struct* g_psNotifyWorkQueue = NULL;
29
30static struct mutex gsVsyncStampLock;
31
32
33typedef struct GED_NOTIFY_SW_SYNC_TAG
34{
35 struct work_struct sWork;
36 unsigned long t;
37 long phase;
38 unsigned long ul3DFenceDoneTime;
39} GED_NOTIFY_SW_SYNC;
40
41
42
43static void ged_notify_sw_sync_work_handle(struct work_struct *psWork)
44{
45 GED_NOTIFY_SW_SYNC* psNotify = GED_CONTAINER_OF(psWork, GED_NOTIFY_SW_SYNC, sWork);
46 if (psNotify)
47 {
48 ged_dvfs_run(psNotify->t, psNotify->phase, psNotify->ul3DFenceDoneTime);
49 ged_free(psNotify, sizeof(GED_NOTIFY_SW_SYNC));
50 }
51}
52
53#define GED_VSYNC_MISS_QUANTUM_NS 16666666
54extern GED_LOG_BUF_HANDLE ghLogBuf_DVFS;
55
56static unsigned long long sw_vsync_ts;
57#ifdef ENABLE_COMMON_DVFS
58static unsigned long long hw_vsync_ts;
59#endif
60static unsigned long long g_ns_gpu_on_ts=0;
61
62static bool g_timer_on = false;
63static unsigned long long g_timer_on_ts=0;
64
65static bool g_bGPUClock = false;
66
67/*
68 * void timer_switch(bool bTock)
69 * only set the staus, not really operating on real timer
70 */
71void timer_switch(bool bTock)
72{
73 mutex_lock(&gsVsyncStampLock);
74 g_timer_on = bTock;
75 if(bTock)
76 {
77 g_timer_on_ts = ged_get_time();
78 }
79 mutex_unlock(&gsVsyncStampLock);
80}
81
82void timer_switch_locked(bool bTock)
83{
84 g_timer_on = bTock;
85 if(bTock)
86 {
87 g_timer_on_ts = ged_get_time();
88 }
89}
90
91
92static void ged_timer_switch_work_handle(struct work_struct *psWork)
93{
94 GED_NOTIFY_SW_SYNC* psNotify = GED_CONTAINER_OF(psWork, GED_NOTIFY_SW_SYNC, sWork);
95 if (psNotify)
96 {
97 timer_switch(false);
98 ged_free(psNotify, sizeof(GED_NOTIFY_SW_SYNC));
99 }
100}
101
102extern unsigned int g_gpu_timer_based_emu;
103GED_ERROR ged_notify_sw_vsync(GED_VSYNC_TYPE eType, GED_DVFS_UM_QUERY_PACK* psQueryData)
104{
105#ifdef ENABLE_COMMON_DVFS
106
107 long long llDiff = 0;
108 bool bHWEventKick = false;
109 unsigned long long temp;
110
111 unsigned long t;
112 long phase = 0;
113
114 temp = ged_get_time();
115
116 if(g_gpu_timer_based_emu)
117 {
118 ged_log_buf_print(ghLogBuf_DVFS, "[GED_K] Vsync ignored (ts=%llu)", temp);
119 return GED_INTENTIONAL_BLOCK;
120 }
121
122
123
124 /*critical session begin*/
125 mutex_lock(&gsVsyncStampLock);
126
127 if(GED_VSYNC_SW_EVENT==eType)
128 {
129 sw_vsync_ts = temp;
130#ifdef ENABLE_TIMER_BACKUP
131 if(hrtimer_start(&g_HT_hwvsync_emu, ns_to_ktime(GED_DVFS_TIMER_TIMEOUT), HRTIMER_MODE_REL)) // timer not start
132 {
133 hrtimer_try_to_cancel ( &g_HT_hwvsync_emu );
134 hrtimer_restart(&g_HT_hwvsync_emu);
135 ged_log_buf_print(ghLogBuf_DVFS, "[GED_K] Timer Restart (ts=%llu)", temp);
136 }
137 else // timer active
138 {
139 ged_log_buf_print(ghLogBuf_DVFS, "[GED_K] New Timer Start (ts=%llu)", temp);
140 timer_switch_locked(true);
141 }
142
143#endif
144 }
145 else
146 {
147 hw_vsync_ts = temp;
148
149 llDiff = (long long)(hw_vsync_ts - sw_vsync_ts);
150
151 if(llDiff > GED_VSYNC_MISS_QUANTUM_NS)
152 {
153 bHWEventKick = true;
154 }
155 }
156#ifdef GED_DVFS_DEBUG
157 if(GED_VSYNC_HW_EVENT==eType)
158 {
159 GED_LOGE("[5566] HW VSYNC: llDiff= %lld, hw_vsync_ts=%llu, sw_vsync_ts=%llu\n", llDiff, hw_vsync_ts, sw_vsync_ts);
160 }
161 else
162 {
163 GED_LOGE("[5566] SW VSYNC: llDiff= %lld, hw_vsync_ts=%llu, sw_vsync_ts=%llu\n", llDiff, hw_vsync_ts, sw_vsync_ts);
164 }
165#endif
166
167
168 if(GED_VSYNC_HW_EVENT==eType)
169 ged_log_buf_print(ghLogBuf_DVFS, "[GED_K] HW VSYNC (ts=%llu) ", hw_vsync_ts);
170 else
171 ged_log_buf_print(ghLogBuf_DVFS, "[GED_K] SW VSYNC (ts=%llu) ", sw_vsync_ts);
172
173 mutex_unlock(&gsVsyncStampLock);
174 /*critical session end*/
175
176 if(GED_VSYNC_SW_EVENT==eType)
177 {
178 do_div(temp,1000);
179 t = (unsigned long)(temp);
180 ged_dvfs_run(t, phase, ged_monitor_3D_fence_done_time());
181 psQueryData->usT = t;
182 psQueryData-> ul3DFenceDoneTime = ged_monitor_3D_fence_done_time();
183 ged_dvfs_sw_vsync_query_data(psQueryData);
184 }
185 else
186 {
187 if(bHWEventKick)
188 {
189#ifdef GED_DVFS_DEBUG
190 GED_LOGE("[5566] HW Event: kick!\n");
191#endif
192 ged_log_buf_print(ghLogBuf_DVFS, "[GED_K] HW VSync: mending kick!");
193 ged_dvfs_run(0, 0, 0);
194 }
195 }
196
197#else
198#if 0
199 GED_NOTIFY_SW_SYNC* psNotify;
200 unsigned long long temp = cpu_clock(smp_processor_id());
201 *pt = (unsigned long)(temp / 1000);
202
203 psNotify = (GED_NOTIFY_SW_SYNC*)ged_alloc(sizeof(GED_NOTIFY_SW_SYNC));
204 if (!psNotify)
205 {
206 return GED_ERROR_OOM;
207 }
208
209 INIT_WORK(&psNotify->sWork, ged_notify_sw_sync_work_handle);
210 psNotify->t = *pt;
211 psNotify->phase = phase;
212 psNotify->ul3DFenceDoneTime = ged_monitor_3D_fence_done_time();
213 queue_work(g_psNotifyWorkQueue, &psNotify->sWork);
214#endif
215#endif
216
217 return GED_OK;
218}
219
220extern unsigned int gpu_loading;
221enum hrtimer_restart ged_sw_vsync_check_cb( struct hrtimer *timer )
222{
223 unsigned long long temp;
224 long long llDiff;
225 GED_NOTIFY_SW_SYNC* psNotify;
226
227 temp = cpu_clock(smp_processor_id()); // interrupt contex no need to set non-preempt
228
229 llDiff = (long long)(temp - sw_vsync_ts);
230
231 if(llDiff > GED_VSYNC_MISS_QUANTUM_NS)
232 {
233 psNotify = (GED_NOTIFY_SW_SYNC*)ged_alloc_atomic(sizeof(GED_NOTIFY_SW_SYNC));
234
235 if(false==g_bGPUClock && 0==gpu_loading && (temp - g_ns_gpu_on_ts> GED_DVFS_TIMER_TIMEOUT) )
236 {
237 if (psNotify)
238 {
239 INIT_WORK(&psNotify->sWork, ged_timer_switch_work_handle);
240 queue_work(g_psNotifyWorkQueue, &psNotify->sWork);
241 }
242 ged_log_buf_print(ghLogBuf_DVFS, "[GED_K] Timer removed (ts=%llu) ", temp);
243 return HRTIMER_NORESTART;
244 }
245
246
247 if (psNotify)
248 {
249 INIT_WORK(&psNotify->sWork, ged_notify_sw_sync_work_handle);
250 psNotify->t = temp;
251 do_div(psNotify->t,1000);
252 psNotify->phase = GED_DVFS_FALLBACK;
253 psNotify->ul3DFenceDoneTime = 0;
254 queue_work(g_psNotifyWorkQueue, &psNotify->sWork);
255 ged_log_buf_print(ghLogBuf_DVFS, "[GED_K] Timer kick (ts=%llu) ", temp);
256 hrtimer_start(&g_HT_hwvsync_emu, ns_to_ktime(GED_DVFS_TIMER_TIMEOUT), HRTIMER_MODE_REL);
257 g_timer_on_ts = temp;
258 }
259 }
260 return HRTIMER_NORESTART;
261}
262
263bool ged_gpu_power_on_notified = 0;
264bool ged_gpu_power_off_notified = 0;
265void ged_dvfs_gpu_clock_switch_notify(bool bSwitch)
266{
267#ifdef ENABLE_COMMON_DVFS
268#ifdef ENABLE_TIMER_BACKUP
269
270 if(bSwitch)
271 {
272 ged_gpu_power_on_notified = true;
273 g_ns_gpu_on_ts = ged_get_time();
274 g_bGPUClock = true;
275 if( g_timer_on )
276 {
277 ged_log_buf_print(ghLogBuf_DVFS, "[GED_K] Timer Already Start");
278 }
279 else
280 {
281 hrtimer_start(&g_HT_hwvsync_emu, ns_to_ktime(GED_DVFS_TIMER_TIMEOUT), HRTIMER_MODE_REL);
282 ged_log_buf_print(ghLogBuf_DVFS, "[GED_K] HW Start Timer");
283 timer_switch(true);
284 }
285 }
286 else
287 {
288 ged_gpu_power_off_notified = true;
289 g_bGPUClock = false;
290 }
291#endif
292#endif
293}
294EXPORT_SYMBOL(ged_dvfs_gpu_clock_switch_notify);
295
296
297#define GED_TIMER_BACKUP_THRESHOLD 3000
298
299/*
300 * SODI implementation need to cancel timer physically.
301 * but timer status is logically unchanged *
302 */
303
304/*
305 * enter sodi state is trivial, just cancel timer
306 */
307void ged_sodi_start(void)
308{
309#ifdef ENABLE_COMMON_DVFS
310 hrtimer_try_to_cancel(&g_HT_hwvsync_emu);
311#endif
312}
313
314
315/*
316 * exit sodi state should aware sands of time is still running
317 */
318void ged_sodi_stop(void)
319{
320#ifdef ENABLE_COMMON_DVFS
321 unsigned long long ns_cur_time;
322 unsigned long long ns_timer_remains;
323 if(g_timer_on)
324 {
325 ns_cur_time = ged_get_time();
326 ns_timer_remains = ns_cur_time - g_timer_on_ts - GED_DVFS_TIMER_TIMEOUT;
327 if( ns_timer_remains < GED_TIMER_BACKUP_THRESHOLD ) // sleeped too long, do timber-based DVFS now
328 {
329 GED_NOTIFY_SW_SYNC* psNotify;
330 psNotify = (GED_NOTIFY_SW_SYNC*)ged_alloc_atomic(sizeof(GED_NOTIFY_SW_SYNC));
331 if (psNotify)
332 {
333 INIT_WORK(&psNotify->sWork, ged_notify_sw_sync_work_handle);
334 psNotify->t = ns_cur_time;
335 psNotify->phase = GED_DVFS_FALLBACK;
336 psNotify->ul3DFenceDoneTime = 0;
337 queue_work(g_psNotifyWorkQueue, &psNotify->sWork);
338 }
339 hrtimer_start(&g_HT_hwvsync_emu, ns_to_ktime(GED_DVFS_TIMER_TIMEOUT), HRTIMER_MODE_REL);
340 }
341 else if( ns_timer_remains > GED_DVFS_TIMER_TIMEOUT)
342 {
343 // unknown status, just start timer with default timeout;
344 hrtimer_start(&g_HT_hwvsync_emu, ns_to_ktime(GED_DVFS_TIMER_TIMEOUT), HRTIMER_MODE_REL);
345 }
346 else // keep counting down the timer with real remianing time
347 {
348 hrtimer_start(&g_HT_hwvsync_emu, ns_to_ktime(ns_timer_remains), HRTIMER_MODE_REL);
349 }
350 }
351#endif
352}
353
354
355GED_ERROR ged_notify_sw_vsync_system_init(void)
356{
357 g_psNotifyWorkQueue = create_workqueue("ged_notify_sw_vsync");
358
359 if (g_psNotifyWorkQueue == NULL)
360 {
361 return GED_ERROR_OOM;
362 }
363 mutex_init(&gsVsyncStampLock);
364
365#ifdef ENABLE_COMMON_DVFS
366#ifdef ENABLE_TIMER_BACKUP
367 hrtimer_init(&g_HT_hwvsync_emu, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
368 g_HT_hwvsync_emu.function = ged_sw_vsync_check_cb;
369
370 mtk_gpu_sodi_entry_fp= ged_sodi_start;
371 mtk_gpu_sodi_exit_fp= ged_sodi_stop;
372
373
374#endif
375#endif
376
377 return GED_OK;
378}
379
380void ged_notify_sw_vsync_system_exit(void)
381{
382 if (g_psNotifyWorkQueue != NULL)
383 {
384 flush_workqueue(g_psNotifyWorkQueue);
385
386 destroy_workqueue(g_psNotifyWorkQueue);
387
388 g_psNotifyWorkQueue = NULL;
389 }
390#ifdef ENABLE_COMMON_DVFS
391 hrtimer_cancel( &g_HT_hwvsync_emu );
392#endif
393 mutex_destroy(&gsVsyncStampLock);
394}