drivers:misc: Setup gamepad_reset gpio only on loki
[linux-3.10.git] / drivers / misc / tegra-throughput.c
1 /*
2  * Copyright (c) 2012-2014, NVIDIA CORPORATION.  All rights reserved.
3  *
4  * This program is free software; you can redistribute it and/or modify it
5  * under the terms and conditions of the GNU General Public License,
6  * version 2, as published by the Free Software Foundation.
7  *
8  * This program is distributed in the hope 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
14  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
15  */
16
17 #include <linux/kthread.h>
18 #include <linux/ktime.h>
19 #include <linux/miscdevice.h>
20 #include <linux/fs.h>
21 #include <linux/debugfs.h>
22 #include <linux/init.h>
23 #include <linux/spinlock.h>
24 #include <linux/throughput_ioctl.h>
25 #include <linux/module.h>
26 #include <linux/nvhost.h>
27 #include <linux/notifier.h>
28 #include <linux/tegra-throughput.h>
29 #include <mach/dc.h>
30
31 #define CREATE_TRACE_POINTS
32 #include <trace/events/tegra_throughput.h>
33
34 #define DEFAULT_SYNC_RATE 60000 /* 60 Hz */
35
36 static unsigned int target_frame_time;
37 static ktime_t last_flip;
38 static spinlock_t lock;
39
40 #define EMA_PERIOD  8
41
42 static int frame_time_sum_init = 1;
43 static long frame_time_sum; /* used for fps EMA */
44 static u64 framecount;
45 static u64 frame_timestamp;
46
47 static struct work_struct work;
48 static int throughput_hint;
49
50 static int sync_rate;
51 static int throughput_active_app_count;
52
53 BLOCKING_NOTIFIER_HEAD(throughput_notifier_list);
54 EXPORT_SYMBOL(throughput_notifier_list);
55
56 static void set_throughput_hint(struct work_struct *work)
57 {
58         trace_tegra_throughput_hint(throughput_hint);
59
60         /* notify throughput hint clients here */
61         blocking_notifier_call_chain(&throughput_notifier_list,
62                                      throughput_hint, NULL);
63 }
64
65 int tegra_throughput_get_hint(void)
66 {
67         return throughput_hint;
68 }
69 EXPORT_SYMBOL(tegra_throughput_get_hint);
70
71 static void throughput_flip_callback(void)
72 {
73         long timediff;
74         ktime_t now;
75
76         now = ktime_get();
77         spin_lock(&lock);
78         framecount++;
79         frame_timestamp = ktime_to_us(now);
80         spin_unlock(&lock);
81
82         if (last_flip.tv64 != 0) {
83                 timediff = (long) ktime_us_delta(now, last_flip);
84                 trace_tegra_throughput_flip(timediff);
85
86                 if (timediff <= 0) {
87                         pr_debug("%s: flips %lld nsec apart\n",
88                                 __func__, now.tv64 - last_flip.tv64);
89                         last_flip = now;
90                         return;
91                 }
92
93                 if (target_frame_time == 0)
94                         return;
95
96                 throughput_hint =
97                         ((int) target_frame_time * 1000) / timediff;
98
99                 trace_tegra_throughput_gen_hint(throughput_hint, timediff);
100
101                 /* only deliver throughput hints when a single app is active */
102                 if (throughput_active_app_count == 1 && !work_pending(&work))
103                         schedule_work(&work);
104
105                 if (frame_time_sum_init) {
106                         frame_time_sum = timediff * EMA_PERIOD;
107                         frame_time_sum_init = 0;
108                 } else {
109                         int t = (frame_time_sum / EMA_PERIOD) *
110                                 (EMA_PERIOD - 1);
111                         frame_time_sum = t + timediff;
112                 }
113         }
114
115         last_flip = now;
116 }
117
118 static void reset_target_frame_time(void)
119 {
120         if (sync_rate == 0) {
121                 sync_rate = tegra_dc_get_panel_sync_rate();
122
123                 if (sync_rate == 0)
124                         sync_rate = DEFAULT_SYNC_RATE;
125         }
126
127         target_frame_time = (unsigned int) (1000000000 / sync_rate);
128
129         pr_debug("%s: panel sync rate %d, target frame time %u\n",
130                 __func__, sync_rate, target_frame_time);
131 }
132
133 static int throughput_open(struct inode *inode, struct file *file)
134 {
135         spin_lock(&lock);
136
137         throughput_active_app_count++;
138         frame_time_sum_init = 1;
139
140         trace_tegra_throughput_open(throughput_active_app_count);
141
142         spin_unlock(&lock);
143
144
145         pr_debug("throughput_open node %p file %p\n", inode, file);
146
147         return 0;
148 }
149
150 static int throughput_release(struct inode *inode, struct file *file)
151 {
152         spin_lock(&lock);
153
154         throughput_active_app_count--;
155         frame_time_sum_init = 1;
156
157         trace_tegra_throughput_release(throughput_active_app_count);
158
159         if (throughput_active_app_count == 1)
160                 reset_target_frame_time();
161
162         spin_unlock(&lock);
163
164         pr_debug("throughput_release node %p file %p\n", inode, file);
165
166         return 0;
167 }
168
169 static int throughput_set_target_fps(unsigned long arg)
170 {
171         pr_debug("%s: target fps %lu requested\n", __func__, arg);
172         trace_tegra_throughput_set_target_fps(arg);
173
174         if (throughput_active_app_count != 1) {
175                 pr_debug("%s: %d active apps, disabling fps usage\n",
176                         __func__, throughput_active_app_count);
177                 return 0;
178         }
179
180         if (arg == 0)
181                 reset_target_frame_time();
182         else
183                 target_frame_time = (unsigned int) (1000000 / arg);
184
185         return 0;
186 }
187
188 static long
189 throughput_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
190 {
191         int err = 0;
192
193         if ((_IOC_TYPE(cmd) != TEGRA_THROUGHPUT_MAGIC) ||
194                 (_IOC_NR(cmd) == 0) ||
195                 (_IOC_NR(cmd) > TEGRA_THROUGHPUT_IOCTL_MAXNR))
196                 return -EFAULT;
197
198         switch (cmd) {
199         case TEGRA_THROUGHPUT_IOCTL_TARGET_FPS:
200                 pr_debug("%s: TEGRA_THROUGHPUT_IOCTL_TARGET_FPS %lu\n",
201                         __func__, arg);
202                 err = throughput_set_target_fps(arg);
203                 break;
204
205         default:
206                 err = -ENOTTY;
207         }
208
209         return err;
210 }
211
212 static const struct file_operations throughput_user_fops = {
213         .owner                  = THIS_MODULE,
214         .open                   = throughput_open,
215 #ifdef CONFIG_COMPAT
216         .compat_ioctl           = throughput_ioctl,
217 #endif
218         .release                = throughput_release,
219         .unlocked_ioctl         = throughput_ioctl,
220 };
221
222 #define TEGRA_THROUGHPUT_MINOR 1
223
224 static struct miscdevice throughput_miscdev = {
225         .minor = TEGRA_THROUGHPUT_MINOR,
226         .name  = "tegra-throughput",
227         .fops  = &throughput_user_fops,
228         .mode  = 0666,
229 };
230
231 static ssize_t show_fps(struct kobject *kobj,
232         struct attribute *attr, char *buf)
233 {
234         int frame_time_avg;
235         ktime_t now;
236         long timediff;
237         int fps = 0;
238
239         if (frame_time_sum_init)
240                 goto DONE;
241
242         now = ktime_get();
243         timediff = (long) ktime_us_delta(now, last_flip);
244         if (timediff > 1000000)
245                 goto DONE;
246
247         frame_time_avg = frame_time_sum / EMA_PERIOD;
248         fps = frame_time_avg > 0 ? 1000000 / frame_time_avg : 0;
249
250 DONE:
251         return sprintf(buf, "%d\n", fps);
252 }
253
254 static struct global_attr fps_attr = __ATTR(fps, 0444,
255                 show_fps, NULL);
256
257 static ssize_t show_framecount(struct kobject *kobj,
258         struct attribute *attr, char *buf)
259 {
260         u64 fcount;
261         u64 fstamp;
262
263         spin_lock(&lock);
264         fcount = framecount;
265         fstamp = frame_timestamp;
266         spin_unlock(&lock);
267
268         return sprintf(buf, "%llu %llu\n",
269                        fcount, fstamp);
270 }
271
272 static struct global_attr framecount_attr = __ATTR(framecount, 0444,
273                 show_framecount, NULL);
274
275 static int __init throughput_init_miscdev(void)
276 {
277         int ret_md, ret_f1, ret_f2;
278
279         pr_debug("%s: initializing\n", __func__);
280
281         spin_lock_init(&lock);
282         INIT_WORK(&work, set_throughput_hint);
283
284         ret_md = misc_register(&throughput_miscdev);
285         ret_f1 = sysfs_create_file(&throughput_miscdev.this_device->kobj,
286                                    &fps_attr.attr);
287         ret_f2 = sysfs_create_file(&throughput_miscdev.this_device->kobj,
288                                    &framecount_attr.attr);
289
290         if (ret_md == 0 && ret_f1 == 0 && ret_f2 == 0) {
291                 tegra_dc_set_flip_callback(throughput_flip_callback);
292
293                 return 0;
294         }
295
296         if (ret_f2 == 0)
297                 sysfs_remove_file(&throughput_miscdev.this_device->kobj,
298                                   &framecount_attr.attr);
299         if (ret_f1 == 0)
300                 sysfs_remove_file(&throughput_miscdev.this_device->kobj,
301                                   &fps_attr.attr);
302         if (ret_md == 0)
303                 misc_deregister(&throughput_miscdev);
304
305         if (ret_md)
306                 return ret_md;
307         if (ret_f1)
308                 return ret_f1;
309         return ret_f2;
310 }
311
312 module_init(throughput_init_miscdev);
313
314 static void __exit throughput_exit_miscdev(void)
315 {
316         pr_debug("%s: exiting\n", __func__);
317
318         tegra_dc_unset_flip_callback();
319
320         cancel_work_sync(&work);
321
322         sysfs_remove_file(&throughput_miscdev.this_device->kobj,
323                           &framecount_attr.attr);
324         sysfs_remove_file(&throughput_miscdev.this_device->kobj,
325                           &fps_attr.attr);
326
327         misc_deregister(&throughput_miscdev);
328 }
329
330 module_exit(throughput_exit_miscdev);
331