ged_notify_sw_vsync.c 13 KB

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