Revert "Fix compilation errors due to merge"
[linux-2.6.git] / drivers / misc / tegra-throughput.c
1 /*
2  * drivers/misc/throughput.c
3  *
4  * Copyright (C) 2012, 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/notifier.h>
23 #include <linux/miscdevice.h>
24 #include <linux/fs.h>
25 #include <linux/init.h>
26 #include <linux/spinlock.h>
27 #include <linux/throughput_ioctl.h>
28 #include <linux/nvhost.h>
29 #include <mach/dc.h>
30
31 #define DEFAULT_SYNC_RATE 60000 /* 60 Hz */
32
33 static unsigned short target_frame_time;
34 static unsigned short last_frame_time;
35 static ktime_t last_flip;
36 static unsigned int multiple_app_disable;
37
38 static spinlock_t lock;
39
40 static int throughput_flip_notifier(struct notifier_block *nb,
41                                              unsigned long val,
42                                              void *data)
43 {
44         /* only register flips when a single display is active */
45         if (val != 1 || multiple_app_disable)
46                 return NOTIFY_DONE;
47         else {
48                 long timediff;
49                 ktime_t now;
50                 int throughput_hint;
51
52                 now = ktime_get();
53                 if (last_flip.tv64 != 0) {
54                         timediff = (long) ktime_us_delta(now, last_flip);
55                         if (timediff > (long) USHRT_MAX)
56                                 last_frame_time = USHRT_MAX;
57                         else
58                                 last_frame_time = (unsigned short) timediff;
59
60                         throughput_hint =
61                                 ((int) target_frame_time * 100)/last_frame_time;
62
63                         /* notify throughput hint clients here */
64                         nvhost_scale3d_set_throughput_hint(throughput_hint);
65                 }
66                 last_flip = now;
67         }
68
69         return NOTIFY_OK;
70 }
71
72 static struct notifier_block throughput_flip_nb = {
73         .notifier_call = throughput_flip_notifier,
74 };
75
76 static int sync_rate;
77 static int throughput_active_app_count;
78
79 static void reset_target_frame_time(void)
80 {
81         if (sync_rate == 0) {
82                 sync_rate = tegra_dc_get_panel_sync_rate();
83
84                 if (sync_rate == 0)
85                         sync_rate = DEFAULT_SYNC_RATE;
86         }
87
88         target_frame_time = (unsigned short) (1000000000 / sync_rate);
89
90         pr_debug("%s: panel sync rate %d, target frame time %u\n",
91                 __func__, sync_rate, target_frame_time);
92 }
93
94 static int notifier_initialized;
95
96 static int throughput_open(struct inode *inode, struct file *file)
97 {
98         if (!notifier_initialized) {
99                 tegra_dc_register_flip_notifier(&throughput_flip_nb);
100                 notifier_initialized = 1;
101         }
102
103         spin_lock(&lock);
104
105         throughput_active_app_count++;
106         if (throughput_active_app_count > 1)
107                 multiple_app_disable = 1;
108
109         spin_unlock(&lock);
110
111         pr_debug("throughput_open node %p file %p\n", inode, file);
112
113         return 0;
114 }
115
116 static int throughput_release(struct inode *inode, struct file *file)
117 {
118         spin_lock(&lock);
119         throughput_active_app_count--;
120         spin_unlock(&lock);
121
122         if (throughput_active_app_count == 0) {
123                 reset_target_frame_time();
124                 multiple_app_disable = 0;
125                 tegra_dc_unregister_flip_notifier(&throughput_flip_nb);
126                 notifier_initialized = 0;
127         }
128
129
130         pr_debug("throughput_release node %p file %p\n", inode, file);
131
132         return 0;
133 }
134
135 static int throughput_set_target_fps(unsigned long arg)
136 {
137         int disable;
138
139         pr_debug("%s: target fps %lu requested\n", __func__, arg);
140
141         disable = multiple_app_disable;
142
143         if (disable) {
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                 unsigned long frame_time = (1000000 / arg);
153
154                 if (frame_time > USHRT_MAX)
155                         frame_time = USHRT_MAX;
156
157                 target_frame_time = (unsigned short) frame_time;
158         }
159
160         return 0;
161 }
162
163 static long
164 throughput_ioctl(struct file *file,
165                           unsigned int cmd,
166                           unsigned long arg)
167 {
168         int err = 0;
169
170         if ((_IOC_TYPE(cmd) != TEGRA_THROUGHPUT_MAGIC) ||
171                 (_IOC_NR(cmd) == 0) ||
172                 (_IOC_NR(cmd) > TEGRA_THROUGHPUT_IOCTL_MAXNR))
173                 return -EFAULT;
174
175         switch (cmd) {
176         case TEGRA_THROUGHPUT_IOCTL_TARGET_FPS:
177                 pr_debug("%s: TEGRA_THROUGHPUT_IOCTL_TARGET_FPS %lu\n",
178                         __func__, arg);
179                 err = throughput_set_target_fps(arg);
180                 break;
181
182         default:
183                 err = -ENOTTY;
184         }
185
186         return err;
187 }
188
189 static const struct file_operations throughput_user_fops = {
190         .owner                  = THIS_MODULE,
191         .open                   = throughput_open,
192         .release                = throughput_release,
193         .unlocked_ioctl         = throughput_ioctl,
194 };
195
196 #define TEGRA_THROUGHPUT_MINOR 1
197
198 static struct miscdevice throughput_miscdev = {
199         .minor  = TEGRA_THROUGHPUT_MINOR,
200         .name   = "tegra-throughput",
201         .fops   = &throughput_user_fops,
202         .mode   = 0666,
203 };
204
205 int __init throughput_init_miscdev(void)
206 {
207         int ret;
208
209         pr_debug("%s: initializing\n", __func__);
210
211         spin_lock_init(&lock);
212
213         ret = misc_register(&throughput_miscdev);
214         if (ret) {
215                 pr_err("can\'t reigster throughput miscdev"
216                        " (minor %d err %d)\n", TEGRA_THROUGHPUT_MINOR, ret);
217                 return ret;
218         }
219
220         return 0;
221 }
222
223 module_init(throughput_init_miscdev);
224
225 void __exit throughput_exit_miscdev(void)
226 {
227         pr_debug("%s: exiting\n", __func__);
228
229         misc_deregister(&throughput_miscdev);
230 }
231
232 module_exit(throughput_exit_miscdev);
233