misc: Removed warnings from MAX1749 driver
[linux-2.6.git] / drivers / misc / tegra-throughput.c
1 /*
2  * drivers/misc/throughput.c
3  *
4  * Copyright (C) 2012-2013, NVIDIA CORPORATION. All rights reserved.
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, version 2 of the License.
9  *
10  * This program is distributed in the hope that it will be useful, but WITHOUT
11  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
13  * more details.
14  *
15  * You should have received a copy of the GNU General Public License along
16  * with this program; if not, write to the Free Software Foundation, Inc.,
17  * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
18  */
19
20 #include <linux/kthread.h>
21 #include <linux/ktime.h>
22 #include <linux/miscdevice.h>
23 #include <linux/fs.h>
24 #include <linux/debugfs.h>
25 #include <linux/init.h>
26 #include <linux/spinlock.h>
27 #include <linux/throughput_ioctl.h>
28 #include <linux/module.h>
29 #include <linux/nvhost.h>
30 #include <mach/dc.h>
31
32 #define DEFAULT_SYNC_RATE 60000 /* 60 Hz */
33
34 static unsigned int target_frame_time;
35 static ktime_t last_flip;
36 static spinlock_t lock;
37
38 #define EMA_PERIOD  8
39
40 static int frame_time_sum_init = 1;
41 static long frame_time_sum; /* used for fps EMA */
42
43 static struct work_struct work;
44 static int throughput_hint;
45
46 static int sync_rate;
47 static int throughput_active_app_count;
48
49 static void set_throughput_hint(struct work_struct *work)
50 {
51         /* notify throughput hint clients here */
52         nvhost_scale3d_set_throughput_hint(throughput_hint);
53 }
54
55 static void throughput_flip_callback(void)
56 {
57         long timediff;
58         ktime_t now;
59
60         now = ktime_get();
61
62         if (last_flip.tv64 != 0) {
63                 timediff = (long) ktime_us_delta(now, last_flip);
64
65                 if (timediff <= 0) {
66                         pr_warn("%s: flips %lld nsec apart\n",
67                                 __func__, now.tv64 - last_flip.tv64);
68                         last_flip = now;
69                         return;
70                 }
71
72                 throughput_hint =
73                         ((int) target_frame_time * 1000) / timediff;
74
75                 /* only deliver throughput hints when a single app is active */
76                 if (throughput_active_app_count == 1 && !work_pending(&work))
77                         schedule_work(&work);
78
79                 if (frame_time_sum_init) {
80                         frame_time_sum = timediff * EMA_PERIOD;
81                         frame_time_sum_init = 0;
82                 } else {
83                         int t = (frame_time_sum / EMA_PERIOD) *
84                                 (EMA_PERIOD - 1);
85                         frame_time_sum = t + timediff;
86                 }
87         }
88
89         last_flip = now;
90 }
91
92 static void reset_target_frame_time(void)
93 {
94         if (sync_rate == 0) {
95                 sync_rate = tegra_dc_get_panel_sync_rate();
96
97                 if (sync_rate == 0)
98                         sync_rate = DEFAULT_SYNC_RATE;
99         }
100
101         target_frame_time = (unsigned int) (1000000000 / sync_rate);
102
103         pr_debug("%s: panel sync rate %d, target frame time %u\n",
104                 __func__, sync_rate, target_frame_time);
105 }
106
107 static int throughput_open(struct inode *inode, struct file *file)
108 {
109         spin_lock(&lock);
110
111         throughput_active_app_count++;
112         frame_time_sum_init = 1;
113
114         spin_unlock(&lock);
115
116
117         pr_debug("throughput_open node %p file %p\n", inode, file);
118
119         return 0;
120 }
121
122 static int throughput_release(struct inode *inode, struct file *file)
123 {
124         spin_lock(&lock);
125
126         throughput_active_app_count--;
127         frame_time_sum_init = 1;
128
129         if (throughput_active_app_count == 1)
130                 reset_target_frame_time();
131
132         spin_unlock(&lock);
133
134         pr_debug("throughput_release node %p file %p\n", inode, file);
135
136         return 0;
137 }
138
139 static int throughput_set_target_fps(unsigned long arg)
140 {
141         pr_debug("%s: target fps %lu requested\n", __func__, arg);
142
143         if (throughput_active_app_count != 1) {
144                 pr_debug("%s: %d active apps, disabling fps usage\n",
145                         __func__, throughput_active_app_count);
146                 return 0;
147         }
148
149         if (arg == 0)
150                 reset_target_frame_time();
151         else
152                 target_frame_time = (unsigned int) (1000000 / arg);
153
154         return 0;
155 }
156
157 static long
158 throughput_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
159 {
160         int err = 0;
161
162         if ((_IOC_TYPE(cmd) != TEGRA_THROUGHPUT_MAGIC) ||
163                 (_IOC_NR(cmd) == 0) ||
164                 (_IOC_NR(cmd) > TEGRA_THROUGHPUT_IOCTL_MAXNR))
165                 return -EFAULT;
166
167         switch (cmd) {
168         case TEGRA_THROUGHPUT_IOCTL_TARGET_FPS:
169                 pr_debug("%s: TEGRA_THROUGHPUT_IOCTL_TARGET_FPS %lu\n",
170                         __func__, arg);
171                 err = throughput_set_target_fps(arg);
172                 break;
173
174         default:
175                 err = -ENOTTY;
176         }
177
178         return err;
179 }
180
181 static const struct file_operations throughput_user_fops = {
182         .owner                  = THIS_MODULE,
183         .open                   = throughput_open,
184         .release                = throughput_release,
185         .unlocked_ioctl         = throughput_ioctl,
186 };
187
188 #define TEGRA_THROUGHPUT_MINOR 1
189
190 static struct miscdevice throughput_miscdev = {
191         .minor = TEGRA_THROUGHPUT_MINOR,
192         .name  = "tegra-throughput",
193         .fops  = &throughput_user_fops,
194         .mode  = 0666,
195 };
196
197 static ssize_t show_fps(struct kobject *kobj,
198         struct attribute *attr, char *buf)
199 {
200         int frame_time_avg;
201         ktime_t now;
202         long timediff;
203         int fps = 0;
204
205         if (frame_time_sum_init)
206                 goto DONE;
207
208         now = ktime_get();
209         timediff = (long) ktime_us_delta(now, last_flip);
210         if (timediff > 1000000)
211                 goto DONE;
212
213         frame_time_avg = frame_time_sum / EMA_PERIOD;
214         fps = frame_time_avg > 0 ? 1000000 / frame_time_avg : 0;
215
216 DONE:
217         return sprintf(buf, "%d\n", fps);
218 }
219
220 static struct global_attr fps_attr = __ATTR(fps, 0444,
221                 show_fps, NULL);
222
223 int __init throughput_init_miscdev(void)
224 {
225         int ret;
226
227         pr_debug("%s: initializing\n", __func__);
228
229         spin_lock_init(&lock);
230         INIT_WORK(&work, set_throughput_hint);
231
232         ret = misc_register(&throughput_miscdev);
233         if (ret) {
234                 pr_err("can\'t reigster throughput miscdev"
235                        " (minor %d err %d)\n", TEGRA_THROUGHPUT_MINOR, ret);
236                 return ret;
237         }
238
239         ret = sysfs_create_file(&throughput_miscdev.this_device->kobj,
240                 &fps_attr.attr);
241         if (ret)
242                 pr_err("%s: error %d creating sysfs node\n", __func__, ret);
243
244         tegra_dc_set_flip_callback(throughput_flip_callback);
245
246         return 0;
247 }
248
249 module_init(throughput_init_miscdev);
250
251 void __exit throughput_exit_miscdev(void)
252 {
253         pr_debug("%s: exiting\n", __func__);
254
255         tegra_dc_unset_flip_callback();
256
257         cancel_work_sync(&work);
258
259         sysfs_remove_file(&throughput_miscdev.this_device->kobj, &fps_attr.attr);
260
261         misc_deregister(&throughput_miscdev);
262 }
263
264 module_exit(throughput_exit_miscdev);
265