Commit | Line | Data |
---|---|---|
6fa3eb70 S |
1 | /* |
2 | * (C) Copyright 2010 | |
3 | * MediaTek <www.MediaTek.com> | |
4 | * | |
5 | * MTK GPU Extension Device | |
6 | * | |
7 | */ | |
8 | ||
9 | #include <linux/cdev.h> | |
10 | #include <linux/delay.h> | |
11 | #include <linux/device.h> | |
12 | #include <linux/fs.h> | |
13 | #include <linux/hardirq.h> | |
14 | #include <linux/init.h> | |
15 | #include <linux/kallsyms.h> | |
16 | #include <linux/miscdevice.h> | |
17 | #include <linux/module.h> | |
18 | #include <linux/poll.h> | |
19 | #include <linux/proc_fs.h> | |
20 | #include <linux/wait.h> | |
21 | #include <linux/sched.h> | |
22 | #include <linux/vmalloc.h> | |
02af6beb | 23 | //#include <mach/system.h> |
6fa3eb70 S |
24 | #include <linux/slab.h> |
25 | #include <linux/spinlock.h> | |
26 | #include <linux/semaphore.h> | |
27 | #include <linux/workqueue.h> | |
28 | #include <linux/kthread.h> | |
29 | #include <linux/aee.h> | |
30 | ||
31 | #include "ged_debugFS.h" | |
32 | #include "ged_log.h" | |
02af6beb | 33 | #include "ged_hal.h" |
6fa3eb70 S |
34 | #include "ged_bridge.h" |
35 | #include "ged_profile_dvfs.h" | |
36 | #include "ged_monitor_3D_fence.h" | |
02af6beb S |
37 | #include "ged_notify_sw_vsync.h" |
38 | #include "ged_dvfs.h" | |
39 | ||
6fa3eb70 S |
40 | |
41 | #define GED_DRIVER_DEVICE_NAME "ged" | |
42 | ||
43 | #define GED_IOCTL_PARAM_BUF_SIZE 0x3000 //12KB | |
44 | ||
45 | #ifdef GED_DEBUG | |
46 | #define GED_LOG_BUF_COMMON_GLES "GLES" | |
47 | static GED_LOG_BUF_HANDLE ghLogBuf_GLES = 0; | |
48 | GED_LOG_BUF_HANDLE ghLogBuf_GED = 0; | |
49 | #endif | |
50 | ||
02af6beb S |
51 | #define GED_LOG_BUF_COMMON_HWC "HWC" |
52 | static GED_LOG_BUF_HANDLE ghLogBuf_HWC = 0; | |
53 | #define GED_LOG_BUF_COMMON_FENCE "FENCE" | |
54 | static GED_LOG_BUF_HANDLE ghLogBuf_FENCE = 0; | |
55 | ||
56 | GED_LOG_BUF_HANDLE ghLogBuf_DVFS = 0; | |
57 | GED_LOG_BUF_HANDLE ghLogBuf_ged_srv = 0; | |
58 | ||
59 | ||
60 | ||
6fa3eb70 S |
61 | static void* gvIOCTLParamBuf = NULL; |
62 | ||
63 | /****************************************************************************** | |
64 | * GED File operations | |
65 | *****************************************************************************/ | |
66 | static int ged_open(struct inode *inode, struct file *filp) | |
67 | { | |
02af6beb S |
68 | GED_LOGE("%s:%d:%d\n", __func__, MAJOR(inode->i_rdev), MINOR(inode->i_rdev)); |
69 | return 0; | |
6fa3eb70 S |
70 | } |
71 | ||
72 | static int ged_release(struct inode *inode, struct file *filp) | |
73 | { | |
02af6beb S |
74 | GED_LOGE("%s:%d:%d\n", __func__, MAJOR(inode->i_rdev), MINOR(inode->i_rdev)); |
75 | return 0; | |
6fa3eb70 S |
76 | } |
77 | ||
78 | static unsigned int ged_poll(struct file *file, struct poll_table_struct *ptable) | |
79 | { | |
02af6beb | 80 | return 0; |
6fa3eb70 S |
81 | } |
82 | ||
83 | static ssize_t ged_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos) | |
84 | { | |
02af6beb | 85 | return 0; |
6fa3eb70 S |
86 | } |
87 | ||
88 | static ssize_t ged_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos) | |
89 | { | |
02af6beb | 90 | return 0; |
6fa3eb70 S |
91 | } |
92 | ||
93 | static long ged_dispatch(GED_BRIDGE_PACKAGE *psBridgePackageKM) | |
94 | { | |
02af6beb S |
95 | int ret = -EFAULT; |
96 | void *pvInt, *pvOut; | |
97 | typedef int (ged_bridge_func_type)(void*, void*); | |
98 | ged_bridge_func_type* pFunc = NULL; | |
99 | ||
100 | if ((psBridgePackageKM->i32InBufferSize >=0) && (psBridgePackageKM->i32OutBufferSize >=0) && | |
101 | (psBridgePackageKM->i32InBufferSize + psBridgePackageKM->i32OutBufferSize < GED_IOCTL_PARAM_BUF_SIZE)) | |
102 | { | |
103 | pvInt = gvIOCTLParamBuf; | |
104 | pvOut = (void*)((char*)pvInt + (uintptr_t)psBridgePackageKM->i32InBufferSize); | |
105 | if (psBridgePackageKM->i32InBufferSize > 0) | |
106 | { | |
107 | if (0 != ged_copy_from_user(pvInt, psBridgePackageKM->pvParamIn, psBridgePackageKM->i32InBufferSize)) | |
108 | { | |
109 | GED_LOGE("ged_copy_from_user fail\n"); | |
110 | return ret; | |
111 | } | |
112 | } | |
113 | ||
114 | // we will change the below switch into a function pointer mapping table in the future | |
115 | switch(GED_GET_BRIDGE_ID(psBridgePackageKM->ui32FunctionID)) | |
116 | { | |
117 | case GED_BRIDGE_COMMAND_LOG_BUF_GET: | |
118 | pFunc = (ged_bridge_func_type*)ged_bridge_log_buf_get; | |
119 | break; | |
120 | case GED_BRIDGE_COMMAND_LOG_BUF_WRITE: | |
121 | pFunc = (ged_bridge_func_type*)ged_bridge_log_buf_write; | |
122 | break; | |
123 | case GED_BRIDGE_COMMAND_LOG_BUF_RESET: | |
124 | pFunc = (ged_bridge_func_type*)ged_bridge_log_buf_reset; | |
125 | break; | |
126 | case GED_BRIDGE_COMMAND_BOOST_GPU_FREQ: | |
127 | pFunc = (ged_bridge_func_type*)ged_bridge_boost_gpu_freq; | |
128 | break; | |
129 | case GED_BRIDGE_COMMAND_MONITOR_3D_FENCE: | |
130 | pFunc = (ged_bridge_func_type*)ged_bridge_monitor_3D_fence; | |
131 | break; | |
132 | case GED_BRIDGE_COMMAND_QUERY_INFO: | |
133 | pFunc = (ged_bridge_func_type*)ged_bridge_query_info; | |
134 | break; | |
135 | case GED_BRIDGE_COMMAND_NOTIFY_VSYNC: | |
136 | pFunc = (ged_bridge_func_type*)ged_bridge_notify_vsync; | |
137 | break; | |
138 | case GED_BRIDGE_COMMAND_DVFS_PROBE: | |
139 | pFunc = (ged_bridge_func_type*)ged_bridge_dvfs_probe; | |
140 | break; | |
141 | case GED_BRIDGE_COMMAND_DVFS_UM_RETURN: | |
142 | pFunc = (ged_bridge_func_type*)ged_bridge_dvfs_um_retrun; | |
143 | break; | |
144 | default: | |
145 | GED_LOGE("Unknown Bridge ID: %u\n", GED_GET_BRIDGE_ID(psBridgePackageKM->ui32FunctionID)); | |
146 | break; | |
147 | } | |
148 | ||
149 | if (pFunc) | |
150 | { | |
151 | ret = pFunc(pvInt, pvOut); | |
152 | } | |
153 | ||
154 | if (psBridgePackageKM->i32OutBufferSize > 0) | |
155 | { | |
156 | if (0 != ged_copy_to_user(psBridgePackageKM->pvParamOut, pvOut, psBridgePackageKM->i32OutBufferSize)) | |
157 | { | |
158 | return ret; | |
159 | } | |
160 | } | |
161 | } | |
162 | ||
163 | return ret; | |
6fa3eb70 S |
164 | } |
165 | ||
166 | DEFINE_SEMAPHORE(ged_dal_sem); | |
167 | ||
168 | static long ged_ioctl(struct file *pFile, unsigned int ioctlCmd, unsigned long arg) | |
169 | { | |
02af6beb | 170 | int ret = -EFAULT; |
6fa3eb70 S |
171 | GED_BRIDGE_PACKAGE *psBridgePackageKM, *psBridgePackageUM = (GED_BRIDGE_PACKAGE*)arg; |
172 | GED_BRIDGE_PACKAGE sBridgePackageKM; | |
173 | ||
02af6beb S |
174 | if (down_interruptible(&ged_dal_sem) < 0) |
175 | { | |
176 | GED_LOGE("Fail to down ged_dal_sem\n"); | |
177 | return -ERESTARTSYS; | |
178 | } | |
6fa3eb70 S |
179 | |
180 | psBridgePackageKM = &sBridgePackageKM; | |
02af6beb S |
181 | if (0 != ged_copy_from_user(psBridgePackageKM, psBridgePackageUM, sizeof(GED_BRIDGE_PACKAGE))) |
182 | { | |
183 | GED_LOGE("Fail to ged_copy_from_user\n"); | |
184 | goto unlock_and_return; | |
185 | } | |
6fa3eb70 | 186 | |
02af6beb | 187 | ret = ged_dispatch(psBridgePackageKM); |
6fa3eb70 S |
188 | |
189 | unlock_and_return: | |
02af6beb | 190 | up(&ged_dal_sem); |
6fa3eb70 | 191 | |
02af6beb | 192 | return ret; |
6fa3eb70 S |
193 | } |
194 | ||
195 | #ifdef CONFIG_COMPAT | |
196 | static long ged_ioctl_compat(struct file *pFile, unsigned int ioctlCmd, unsigned long arg) | |
197 | { | |
02af6beb S |
198 | typedef struct GED_BRIDGE_PACKAGE_32_TAG |
199 | { | |
200 | unsigned int ui32FunctionID; | |
201 | int i32Size; | |
202 | unsigned int ui32ParamIn; | |
203 | int i32InBufferSize; | |
204 | unsigned int ui32ParamOut; | |
205 | int i32OutBufferSize; | |
206 | } GED_BRIDGE_PACKAGE_32; | |
207 | ||
208 | int ret = -EFAULT; | |
209 | GED_BRIDGE_PACKAGE sBridgePackageKM64; | |
210 | GED_BRIDGE_PACKAGE_32 sBridgePackageKM32; | |
211 | GED_BRIDGE_PACKAGE_32 *psBridgePackageKM32 = &sBridgePackageKM32; | |
212 | GED_BRIDGE_PACKAGE_32 *psBridgePackageUM32 = (GED_BRIDGE_PACKAGE_32*)arg; | |
213 | ||
214 | if (down_interruptible(&ged_dal_sem) < 0) | |
215 | { | |
216 | GED_LOGE("Fail to down ged_dal_sem\n"); | |
217 | return -ERESTARTSYS; | |
218 | } | |
219 | ||
220 | if (0 != ged_copy_from_user(psBridgePackageKM32, psBridgePackageUM32, sizeof(GED_BRIDGE_PACKAGE_32))) | |
221 | { | |
222 | GED_LOGE("Fail to ged_copy_from_user\n"); | |
223 | goto unlock_and_return; | |
224 | } | |
225 | ||
226 | sBridgePackageKM64.ui32FunctionID = psBridgePackageKM32->ui32FunctionID; | |
227 | sBridgePackageKM64.i32Size = sizeof(GED_BRIDGE_PACKAGE); | |
228 | sBridgePackageKM64.pvParamIn = (void*) ((size_t) psBridgePackageKM32->ui32ParamIn); | |
229 | sBridgePackageKM64.pvParamOut = (void*) ((size_t) psBridgePackageKM32->ui32ParamOut); | |
230 | sBridgePackageKM64.i32InBufferSize = psBridgePackageKM32->i32InBufferSize; | |
231 | sBridgePackageKM64.i32OutBufferSize = psBridgePackageKM32->i32OutBufferSize; | |
232 | ||
233 | ret = ged_dispatch(&sBridgePackageKM64); | |
234 | ||
6fa3eb70 | 235 | unlock_and_return: |
02af6beb S |
236 | up(&ged_dal_sem); |
237 | ||
238 | return ret; | |
6fa3eb70 S |
239 | } |
240 | #endif | |
241 | ||
242 | /****************************************************************************** | |
243 | * Module related | |
244 | *****************************************************************************/ | |
245 | ||
246 | static struct file_operations ged_fops = { | |
02af6beb S |
247 | .owner = THIS_MODULE, |
248 | .open = ged_open, | |
249 | .release = ged_release, | |
250 | .poll = ged_poll, | |
251 | .read = ged_read, | |
252 | .write = ged_write, | |
253 | .unlocked_ioctl = ged_ioctl, | |
6fa3eb70 | 254 | #ifdef CONFIG_COMPAT |
02af6beb | 255 | .compat_ioctl = ged_ioctl_compat, |
6fa3eb70 S |
256 | #endif |
257 | }; | |
258 | ||
259 | #if 0 | |
260 | static struct miscdevice ged_dev = { | |
02af6beb S |
261 | .minor = MISC_DYNAMIC_MINOR, |
262 | .name = "ged", | |
263 | .fops = &ged_fops, | |
6fa3eb70 S |
264 | }; |
265 | #endif | |
266 | ||
267 | static void ged_exit(void) | |
268 | { | |
02af6beb S |
269 | #ifdef GED_DVFS_DEBUG_BUF |
270 | ged_log_buf_free(ghLogBuf_DVFS); | |
271 | ged_log_buf_free(ghLogBuf_ged_srv); | |
272 | ghLogBuf_DVFS = 0; | |
273 | ghLogBuf_ged_srv = 0; | |
274 | #endif | |
6fa3eb70 | 275 | #ifdef GED_DEBUG |
02af6beb S |
276 | ged_log_buf_free(ghLogBuf_GED); |
277 | ghLogBuf_GED = 0; | |
278 | ged_log_buf_free(ghLogBuf_GLES); | |
279 | ghLogBuf_GLES = 0; | |
6fa3eb70 | 280 | #endif |
02af6beb S |
281 | ged_log_buf_free(ghLogBuf_FENCE); |
282 | ghLogBuf_FENCE = 0; | |
283 | ged_log_buf_free(ghLogBuf_HWC); | |
284 | ghLogBuf_HWC = 0; | |
285 | ||
286 | ged_dvfs_system_exit(); | |
287 | ||
288 | ged_profile_dvfs_exit(); | |
289 | ||
290 | //ged_notify_vsync_system_exit(); | |
6fa3eb70 | 291 | |
02af6beb | 292 | ged_notify_sw_vsync_system_exit(); |
6fa3eb70 | 293 | |
02af6beb | 294 | ged_hal_exit(); |
6fa3eb70 | 295 | |
02af6beb | 296 | ged_log_system_exit(); |
6fa3eb70 | 297 | |
02af6beb | 298 | ged_debugFS_exit(); |
6fa3eb70 | 299 | |
02af6beb S |
300 | remove_proc_entry(GED_DRIVER_DEVICE_NAME, NULL); |
301 | ||
302 | if (gvIOCTLParamBuf) | |
303 | { | |
304 | vfree(gvIOCTLParamBuf); | |
305 | gvIOCTLParamBuf = NULL; | |
306 | } | |
6fa3eb70 S |
307 | } |
308 | ||
309 | static int ged_init(void) | |
310 | { | |
02af6beb S |
311 | GED_ERROR err = GED_ERROR_FAIL; |
312 | ||
313 | gvIOCTLParamBuf = vmalloc(GED_IOCTL_PARAM_BUF_SIZE); | |
314 | if (NULL == gvIOCTLParamBuf) | |
315 | { | |
316 | err = GED_ERROR_OOM; | |
317 | goto ERROR; | |
318 | } | |
319 | ||
320 | if (NULL == proc_create(GED_DRIVER_DEVICE_NAME, 0644, NULL, &ged_fops)) | |
321 | { | |
322 | err = GED_ERROR_FAIL; | |
323 | GED_LOGE("ged: failed to register ged proc entry!\n"); | |
324 | goto ERROR; | |
325 | } | |
326 | ||
327 | err = ged_debugFS_init(); | |
328 | if (unlikely(err != GED_OK)) | |
329 | { | |
330 | GED_LOGE("ged: failed to init debug FS!\n"); | |
331 | goto ERROR; | |
332 | } | |
333 | ||
334 | err = ged_log_system_init(); | |
335 | if (unlikely(err != GED_OK)) | |
336 | { | |
337 | GED_LOGE("ged: failed to create gedlog entry!\n"); | |
338 | goto ERROR; | |
339 | } | |
340 | ||
341 | err = ged_hal_init(); | |
342 | if (unlikely(err != GED_OK)) | |
343 | { | |
344 | GED_LOGE("ged: failed to create hal entry!\n"); | |
345 | goto ERROR; | |
346 | } | |
347 | ||
348 | err = ged_notify_sw_vsync_system_init(); | |
349 | if (unlikely(err != GED_OK)) | |
350 | { | |
351 | GED_LOGE("ged: failed to init notify sw vsync!\n"); | |
352 | goto ERROR; | |
353 | } | |
354 | ||
355 | err = ged_profile_dvfs_init(); | |
356 | if (unlikely(err != GED_OK)) | |
357 | { | |
358 | GED_LOGE("ged: failed to init profile dvfs!\n"); | |
359 | goto ERROR; | |
360 | } | |
361 | ||
362 | ||
363 | err = ged_dvfs_system_init(); | |
364 | if (unlikely(err != GED_OK)) | |
365 | { | |
366 | GED_LOGE("ged: failed to init common dvfs!\n"); | |
367 | goto ERROR; | |
368 | } | |
369 | ||
6fa3eb70 S |
370 | |
371 | #ifdef GED_DEBUG | |
02af6beb S |
372 | ghLogBuf_GLES = ged_log_buf_alloc(160, 128 * 160, GED_LOG_BUF_TYPE_RINGBUFFER, GED_LOG_BUF_COMMON_GLES, NULL); |
373 | ghLogBuf_GED = ged_log_buf_alloc(32, 64 * 32, GED_LOG_BUF_TYPE_RINGBUFFER, "GED internal", NULL); | |
374 | #endif | |
375 | ghLogBuf_HWC = ged_log_buf_alloc(4096, 128 * 4096, GED_LOG_BUF_TYPE_RINGBUFFER, GED_LOG_BUF_COMMON_HWC, NULL); | |
376 | ghLogBuf_FENCE = ged_log_buf_alloc(256, 128 * 256, GED_LOG_BUF_TYPE_RINGBUFFER, GED_LOG_BUF_COMMON_FENCE, NULL); | |
377 | ||
378 | #ifdef GED_DVFS_DEBUG_BUF | |
379 | #ifdef GED_LOG_SIZE_LIMITED | |
380 | ghLogBuf_DVFS = ged_log_buf_alloc(20*60, 20*60*80, GED_LOG_BUF_TYPE_RINGBUFFER, "DVFS_Log", "ged_dvfs_debug_limited"); | |
381 | #else | |
382 | ghLogBuf_DVFS = ged_log_buf_alloc(20*60*10, 20*60*10*80, GED_LOG_BUF_TYPE_RINGBUFFER, "DVFS_Log", "ged_dvfs_debug"); | |
6fa3eb70 | 383 | #endif |
02af6beb S |
384 | ghLogBuf_ged_srv = ged_log_buf_alloc(32, 32*80, GED_LOG_BUF_TYPE_RINGBUFFER, "ged_srv_Log", "ged_srv_debug"); |
385 | #endif | |
6fa3eb70 | 386 | |
02af6beb | 387 | return 0; |
6fa3eb70 S |
388 | |
389 | ERROR: | |
02af6beb | 390 | ged_exit(); |
6fa3eb70 | 391 | |
02af6beb | 392 | return -EFAULT; |
6fa3eb70 S |
393 | } |
394 | ||
395 | module_init(ged_init); | |
396 | module_exit(ged_exit); | |
397 | ||
398 | MODULE_LICENSE("GPL"); | |
399 | MODULE_DESCRIPTION("MediaTek GED Driver"); | |
400 | MODULE_AUTHOR("MediaTek Inc."); |