gpu: pvr: pdumpfs: add Kconfig and debugfs pdump mode handling
[sgx.git] / pvr / pvr_events.c
1
2 #include "pvr_events.h"
3 #include "servicesint.h"
4 #include "sgx_bridge.h"
5
6 #include <linux/wait.h>
7 #include <linux/sched.h>
8 #include <linux/spinlock.h>
9 #include <linux/time.h>
10 #include <linux/uaccess.h>
11 #include <linux/fs.h>
12 #include <linux/notifier.h>
13 #include <linux/slab.h>
14
15 #include <plat/display.h>
16
17 static spinlock_t event_lock;
18 static struct list_head sync_wait_list;
19 static struct list_head flip_wait_list;
20 static struct notifier_block dss_nb;
21
22 static inline bool is_render_complete(const struct PVRSRV_SYNC_DATA *sync,
23                                       u32 write_ops_pending)
24 {
25         return (int)sync->ui32WriteOpsComplete - (int)write_ops_pending >= 0;
26 }
27
28 static void pvr_signal_sync_event(struct pvr_pending_sync_event *e,
29                                         const struct timeval *now)
30 {
31         e->event.tv_sec = now->tv_sec;
32         e->event.tv_usec = now->tv_usec;
33
34         list_move_tail(&e->base.link, &e->base.file_priv->event_list);
35
36         wake_up_interruptible(&e->base.file_priv->event_wait);
37 }
38
39 int pvr_sync_event_req(struct PVRSRV_FILE_PRIVATE_DATA *priv,
40                         const struct PVRSRV_KERNEL_SYNC_INFO *sync_info,
41                         u64 user_data)
42 {
43         struct pvr_pending_sync_event *e;
44         struct timeval now;
45         unsigned long flags;
46
47         e = kzalloc(sizeof(*e), GFP_KERNEL);
48         if (e == NULL)
49                 return -ENOMEM;
50
51         e->event.base.type = PVR_EVENT_SYNC;
52         e->event.base.length = sizeof(e->event);
53         e->event.sync_info = sync_info;
54         e->event.user_data = user_data;
55         e->base.event = &e->event.base;
56         e->base.file_priv = priv;
57         e->base.destroy = (void (*)(struct pvr_pending_event *))kfree;
58         e->base.write_ops_pending = sync_info->psSyncData->ui32WriteOpsPending;
59
60         do_gettimeofday(&now);
61         spin_lock_irqsave(&event_lock, flags);
62
63         if (priv->event_space < sizeof(e->event)) {
64                 spin_unlock_irqrestore(&event_lock, flags);
65                 kfree(e);
66                 return -ENOMEM;
67         }
68
69         priv->event_space -= sizeof(e->event);
70
71         list_add_tail(&e->base.link, &sync_wait_list);
72         if (is_render_complete(sync_info->psSyncData,
73                                e->base.write_ops_pending))
74                 pvr_signal_sync_event(e, &now);
75
76         spin_unlock_irqrestore(&event_lock, flags);
77
78         return 0;
79 }
80
81 static void pvr_signal_flip_event(struct pvr_pending_flip_event *e,
82                                         const struct timeval *now)
83 {
84         e->event.tv_sec = now->tv_sec;
85         e->event.tv_usec = now->tv_usec;
86
87         list_move_tail(&e->base.link, &e->base.file_priv->event_list);
88
89         wake_up_interruptible(&e->base.file_priv->event_wait);
90 }
91
92 int pvr_flip_event_req(struct PVRSRV_FILE_PRIVATE_DATA *priv,
93                          unsigned int overlay,
94                          enum pvr_sync_wait_seq_type type, u64 user_data)
95 {
96         struct pvr_pending_flip_event *e;
97         struct timeval now;
98         unsigned long flags;
99
100         e = kzalloc(sizeof(*e), GFP_KERNEL);
101         if (e == NULL)
102                 return -ENOMEM;
103
104         switch (type) {
105         case _PVR_SYNC_WAIT_FLIP:
106                 e->event.base.type = PVR_EVENT_FLIP;
107                 e->dss_event = OMAP_DSS_NOTIFY_GO_OVL;
108                 break;
109         case _PVR_SYNC_WAIT_UPDATE:
110                 e->event.base.type = PVR_EVENT_UPDATE;
111                 e->dss_event = OMAP_DSS_NOTIFY_UPDATE_OVL;
112                 break;
113         default:
114                 BUG();
115         }
116         e->event.base.length = sizeof(e->event);
117         e->base.event = &e->event.base;
118         e->event.overlay = overlay;
119         e->event.user_data = user_data;
120         e->base.file_priv = priv;
121         e->base.destroy = (void (*)(struct pvr_pending_event *))kfree;
122
123         do_gettimeofday(&now);
124         spin_lock_irqsave(&event_lock, flags);
125
126         if (priv->event_space < sizeof(e->event)) {
127                 spin_unlock_irqrestore(&event_lock, flags);
128                 kfree(e);
129                 return -ENOMEM;
130         }
131
132         priv->event_space -= sizeof(e->event);
133
134         list_add_tail(&e->base.link, &flip_wait_list);
135
136         spin_unlock_irqrestore(&event_lock, flags);
137
138         return omap_dss_request_notify(e->dss_event, overlay);
139 }
140
141 static bool pvr_dequeue_event(struct PVRSRV_FILE_PRIVATE_DATA *priv,
142                 size_t total, size_t max, struct pvr_pending_event **out)
143 {
144         struct pvr_pending_event *e;
145         unsigned long flags;
146         bool ret = false;
147
148         spin_lock_irqsave(&event_lock, flags);
149
150         *out = NULL;
151         if (list_empty(&priv->event_list))
152                 goto err;
153
154         e = list_first_entry(&priv->event_list, struct pvr_pending_event, link);
155         if (e->event->length + total > max)
156                 goto err;
157
158         priv->event_space += e->event->length;
159         list_del(&e->link);
160         *out = e;
161         ret = true;
162
163 err:
164         spin_unlock_irqrestore(&event_lock, flags);
165
166         return ret;
167 }
168
169 ssize_t pvr_read(struct file *filp, char __user *buf, size_t count, loff_t *off)
170 {
171         struct PVRSRV_FILE_PRIVATE_DATA *priv = filp->private_data;
172         struct pvr_pending_event *e;
173         size_t total;
174         ssize_t ret;
175
176         ret = wait_event_interruptible(priv->event_wait,
177                                         !list_empty(&priv->event_list));
178
179         if (ret < 0)
180                 return ret;
181
182         total = 0;
183         while (pvr_dequeue_event(priv, total, count, &e)) {
184                 if (copy_to_user(buf + total, e->event, e->event->length)) {
185                         total = -EFAULT;
186                         break;
187                 }
188
189                 total += e->event->length;
190                 e->destroy(e);
191         }
192
193         return total;
194 }
195
196 unsigned int pvr_poll(struct file *filp, struct poll_table_struct *wait)
197 {
198         struct PVRSRV_FILE_PRIVATE_DATA *priv = filp->private_data;
199         unsigned int mask = 0;
200
201         poll_wait(filp, &priv->event_wait, wait);
202
203         if (!list_empty(&priv->event_list))
204                 mask |= POLLIN | POLLRDNORM;
205
206         return mask;
207 }
208
209 void pvr_handle_sync_events(void)
210 {
211         struct pvr_pending_sync_event *e;
212         struct pvr_pending_sync_event *t;
213         struct timeval now;
214         unsigned long flags;
215
216         do_gettimeofday(&now);
217
218         spin_lock_irqsave(&event_lock, flags);
219
220         list_for_each_entry_safe(e, t, &sync_wait_list, base.link) {
221                 if (!is_render_complete(e->event.sync_info->psSyncData,
222                                         e->base.write_ops_pending))
223                         continue;
224
225                 pvr_signal_sync_event(e, &now);
226         }
227
228         spin_unlock_irqrestore(&event_lock, flags);
229 }
230
231 static int dss_notifier_callback(struct notifier_block *nb,
232                                  unsigned long event, void *data)
233 {
234         struct pvr_pending_flip_event *e;
235         struct pvr_pending_flip_event *t;
236         struct timeval now;
237         unsigned long flags;
238         long overlay = (long) data;
239
240         do_gettimeofday(&now);
241
242         spin_lock_irqsave(&event_lock, flags);
243
244         list_for_each_entry_safe(e, t, &flip_wait_list, base.link) {
245                 if (!(e->dss_event & event))
246                         continue;
247                 if (e->event.overlay != overlay)
248                         continue;
249
250                 pvr_signal_flip_event(e, &now);
251         }
252
253         spin_unlock_irqrestore(&event_lock, flags);
254
255         return 0;
256 }
257
258 void pvr_release_events(struct PVRSRV_FILE_PRIVATE_DATA *priv)
259 {
260         struct pvr_pending_event *w;
261         struct pvr_pending_event *z;
262         struct pvr_pending_sync_event *e;
263         struct pvr_pending_sync_event *t;
264         struct pvr_pending_flip_event *e2;
265         struct pvr_pending_flip_event *t2;
266         unsigned long flags;
267
268         spin_lock_irqsave(&event_lock, flags);
269
270         /* Remove pending waits */
271         list_for_each_entry_safe(e, t, &sync_wait_list, base.link)
272                 if (e->base.file_priv == priv) {
273                         list_del(&e->base.link);
274                         e->base.destroy(&e->base);
275                 }
276
277         list_for_each_entry_safe(e2, t2, &flip_wait_list, base.link)
278                 if (e2->base.file_priv == priv) {
279                         list_del(&e2->base.link);
280                         e2->base.destroy(&e2->base);
281                 }
282
283         /* Remove unconsumed events */
284         list_for_each_entry_safe(w, z, &priv->event_list, link)
285                 w->destroy(w);
286
287         spin_unlock_irqrestore(&event_lock, flags);
288 }
289
290 void pvr_init_events(void)
291 {
292         spin_lock_init(&event_lock);
293         INIT_LIST_HEAD(&sync_wait_list);
294         INIT_LIST_HEAD(&flip_wait_list);
295
296         dss_nb.notifier_call = dss_notifier_callback;
297         omap_dss_register_notifier(&dss_nb);
298 }
299
300 void pvr_exit_events(void)
301 {
302         omap_dss_unregister_notifier(&dss_nb);
303 }