DSS2: add support for FBIO_WAITFORVSYNC
[pandora-kernel.git] / drivers / video / omap2 / omapfb / omapfb-ioctl.c
1 /*
2  * linux/drivers/video/omap2/omapfb-ioctl.c
3  *
4  * Copyright (C) 2008 Nokia Corporation
5  * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com>
6  *
7  * Some code and ideas taken from drivers/video/omap/ driver
8  * by Imre Deak.
9  *
10  * This program is free software; you can redistribute it and/or modify it
11  * under the terms of the GNU General Public License version 2 as published by
12  * the Free Software Foundation.
13  *
14  * This program is distributed in the hope that it will be useful, but WITHOUT
15  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
16  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
17  * more details.
18  *
19  * You should have received a copy of the GNU General Public License along with
20  * this program.  If not, see <http://www.gnu.org/licenses/>.
21  */
22
23 #include <linux/fb.h>
24 #include <linux/device.h>
25 #include <linux/uaccess.h>
26 #include <linux/platform_device.h>
27 #include <linux/mm.h>
28 #include <linux/omapfb.h>
29 #include <linux/vmalloc.h>
30
31 #include <mach/display.h>
32 #include <mach/vrfb.h>
33
34 #include "omapfb.h"
35
36 static int omapfb_setup_plane(struct fb_info *fbi, struct omapfb_plane_info *pi)
37 {
38         struct omapfb_info *ofbi = FB2OFB(fbi);
39         struct omapfb2_device *fbdev = ofbi->fbdev;
40         struct omap_display *display = fb2display(fbi);
41         struct omap_overlay *ovl;
42         struct omap_overlay_info info;
43         int r = 0;
44
45         DBG("omapfb_setup_plane\n");
46
47         omapfb_lock(fbdev);
48
49         if (ofbi->num_overlays != 1) {
50                 r = -EINVAL;
51                 goto out;
52         }
53
54         /* XXX uses only the first overlay */
55         ovl = ofbi->overlays[0];
56
57         if (pi->enabled && !ofbi->region.size) {
58                 /*
59                  * This plane's memory was freed, can't enable it
60                  * until it's reallocated.
61                  */
62                 r = -EINVAL;
63                 goto out;
64         }
65
66         ovl->get_overlay_info(ovl, &info);
67
68         info.pos_x = pi->pos_x;
69         info.pos_y = pi->pos_y;
70         info.out_width = pi->out_width;
71         info.out_height = pi->out_height;
72         info.enabled = pi->enabled;
73
74         r = ovl->set_overlay_info(ovl, &info);
75         if (r)
76                 goto out;
77
78         if (ovl->manager) {
79                 r = ovl->manager->apply(ovl->manager);
80                 if (r)
81                         goto out;
82         }
83
84         if (display) {
85                 u16 w, h;
86
87                 if (display->sync)
88                         display->sync(display);
89
90                 display->get_resolution(display, &w, &h);
91
92                 if (display->update)
93                         display->update(display, 0, 0, w, h);
94         }
95
96 out:
97         omapfb_unlock(fbdev);
98         if (r)
99                 dev_err(fbdev->dev, "setup_plane failed\n");
100         return r;
101 }
102
103 static int omapfb_query_plane(struct fb_info *fbi, struct omapfb_plane_info *pi)
104 {
105         struct omapfb_info *ofbi = FB2OFB(fbi);
106         struct omapfb2_device *fbdev = ofbi->fbdev;
107
108         omapfb_lock(fbdev);
109
110         if (ofbi->num_overlays != 1) {
111                 memset(pi, 0, sizeof(*pi));
112         } else {
113                 struct omap_overlay_info *ovli;
114                 struct omap_overlay *ovl;
115
116                 ovl = ofbi->overlays[0];
117                 ovli = &ovl->info;
118
119                 pi->pos_x = ovli->pos_x;
120                 pi->pos_y = ovli->pos_y;
121                 pi->enabled = ovli->enabled;
122                 pi->channel_out = 0; /* xxx */
123                 pi->mirror = 0;
124                 pi->out_width = ovli->out_width;
125                 pi->out_height = ovli->out_height;
126         }
127
128         omapfb_unlock(fbdev);
129
130         return 0;
131 }
132
133 static int omapfb_setup_mem(struct fb_info *fbi, struct omapfb_mem_info *mi)
134 {
135         struct omapfb_info *ofbi = FB2OFB(fbi);
136         struct omapfb2_device *fbdev = ofbi->fbdev;
137         struct omapfb2_mem_region *rg;
138         int r, i;
139         size_t size;
140
141         if (mi->type > OMAPFB_MEMTYPE_MAX)
142                 return -EINVAL;
143
144         size = PAGE_ALIGN(mi->size);
145
146         rg = &ofbi->region;
147
148         omapfb_lock(fbdev);
149
150         for (i = 0; i < ofbi->num_overlays; i++) {
151                 if (ofbi->overlays[i]->info.enabled) {
152                         r = -EBUSY;
153                         goto out;
154                 }
155         }
156
157         if (rg->size != size || rg->type != mi->type) {
158                 r = omapfb_realloc_fbmem(fbi, size, mi->type);
159                 if (r) {
160                         dev_err(fbdev->dev, "realloc fbmem failed\n");
161                         goto out;
162                 }
163         }
164
165         r = 0;
166 out:
167         omapfb_unlock(fbdev);
168
169         return r;
170 }
171
172 static int omapfb_query_mem(struct fb_info *fbi, struct omapfb_mem_info *mi)
173 {
174         struct omapfb_info *ofbi = FB2OFB(fbi);
175         struct omapfb2_device *fbdev = ofbi->fbdev;
176         struct omapfb2_mem_region *rg;
177
178         rg = &ofbi->region;
179         memset(mi, 0, sizeof(*mi));
180
181         omapfb_lock(fbdev);
182         mi->size = rg->size;
183         mi->type = rg->type;
184         omapfb_unlock(fbdev);
185
186         return 0;
187 }
188
189 static int omapfb_update_window(struct fb_info *fbi,
190                 u32 x, u32 y, u32 w, u32 h)
191 {
192         struct omapfb_info *ofbi = FB2OFB(fbi);
193         struct omapfb2_device *fbdev = ofbi->fbdev;
194         struct omap_display *display = fb2display(fbi);
195         u16 dw, dh;
196
197         if (!display)
198                 return 0;
199
200         if (w == 0 || h == 0)
201                 return 0;
202
203         display->get_resolution(display, &dw, &dh);
204
205         if (x + w > dw || y + h > dh)
206                 return -EINVAL;
207
208         omapfb_lock(fbdev);
209         display->update(display, x, y, w, h);
210         omapfb_unlock(fbdev);
211
212         return 0;
213 }
214
215 static int omapfb_set_update_mode(struct fb_info *fbi,
216                                    enum omapfb_update_mode mode)
217 {
218         struct omapfb_info *ofbi = FB2OFB(fbi);
219         struct omapfb2_device *fbdev = ofbi->fbdev;
220         struct omap_display *display = fb2display(fbi);
221         enum omap_dss_update_mode um;
222         int r;
223
224         if (!display || !display->set_update_mode)
225                 return -EINVAL;
226
227         switch (mode) {
228         case OMAPFB_UPDATE_DISABLED:
229                 um = OMAP_DSS_UPDATE_DISABLED;
230                 break;
231
232         case OMAPFB_AUTO_UPDATE:
233                 um = OMAP_DSS_UPDATE_AUTO;
234                 break;
235
236         case OMAPFB_MANUAL_UPDATE:
237                 um = OMAP_DSS_UPDATE_MANUAL;
238                 break;
239
240         default:
241                 return -EINVAL;
242         }
243
244         omapfb_lock(fbdev);
245         r = display->set_update_mode(display, um);
246         omapfb_unlock(fbdev);
247
248         return r;
249 }
250
251 static int omapfb_get_update_mode(struct fb_info *fbi,
252                 enum omapfb_update_mode *mode)
253 {
254         struct omapfb_info *ofbi = FB2OFB(fbi);
255         struct omapfb2_device *fbdev = ofbi->fbdev;
256         struct omap_display *display = fb2display(fbi);
257         enum omap_dss_update_mode m;
258
259         if (!display || !display->get_update_mode)
260                 return -EINVAL;
261
262         omapfb_lock(fbdev);
263         m = display->get_update_mode(display);
264         omapfb_unlock(fbdev);
265
266         switch (m) {
267         case OMAP_DSS_UPDATE_DISABLED:
268                 *mode = OMAPFB_UPDATE_DISABLED;
269                 break;
270         case OMAP_DSS_UPDATE_AUTO:
271                 *mode = OMAPFB_AUTO_UPDATE;
272                 break;
273         case OMAP_DSS_UPDATE_MANUAL:
274                 *mode = OMAPFB_MANUAL_UPDATE;
275                 break;
276         default:
277                 BUG();
278         }
279
280         return 0;
281 }
282
283 /* XXX this color key handling is a hack... */
284 static struct omapfb_color_key omapfb_color_keys[2];
285
286 static int _omapfb_set_color_key(struct omap_overlay_manager *mgr,
287                 struct omapfb_color_key *ck)
288 {
289         enum omap_dss_color_key_type kt;
290
291         if (!mgr->set_default_color ||
292                         !mgr->set_trans_key_type_and_value ||
293                         !mgr->enable_trans_key)
294                 return 0;
295
296         if (ck->key_type == OMAPFB_COLOR_KEY_DISABLED) {
297                 mgr->enable_trans_key(mgr, 0);
298                 omapfb_color_keys[mgr->id] = *ck;
299                 return 0;
300         }
301
302         switch(ck->key_type) {
303         case OMAPFB_COLOR_KEY_GFX_DST:
304                 kt = OMAP_DSS_COLOR_KEY_GFX_DST;
305                 break;
306         case OMAPFB_COLOR_KEY_VID_SRC:
307                 kt = OMAP_DSS_COLOR_KEY_VID_SRC;
308                 break;
309         default:
310                 return -EINVAL;
311         }
312
313         mgr->set_default_color(mgr, ck->background);
314         mgr->set_trans_key_type_and_value(mgr, kt, ck->trans_key);
315         mgr->enable_trans_key(mgr, 1);
316
317         omapfb_color_keys[mgr->id] = *ck;
318
319         return 0;
320 }
321
322 static int omapfb_set_color_key(struct fb_info *fbi,
323                 struct omapfb_color_key *ck)
324 {
325         struct omapfb_info *ofbi = FB2OFB(fbi);
326         struct omapfb2_device *fbdev = ofbi->fbdev;
327         int r;
328         int i;
329         struct omap_overlay_manager *mgr = NULL;
330
331         omapfb_lock(fbdev);
332
333         for (i = 0; i < ofbi->num_overlays; i++) {
334                 if (ofbi->overlays[i]->manager) {
335                         mgr = ofbi->overlays[i]->manager;
336                         break;
337                 }
338         }
339
340         if (!mgr) {
341                 r = -EINVAL;
342                 goto err;
343         }
344
345         if (!mgr->set_default_color ||
346                         !mgr->set_trans_key_type_and_value ||
347                         !mgr->enable_trans_key) {
348                 r = -ENODEV;
349                 goto err;
350         }
351
352         r = _omapfb_set_color_key(mgr, ck);
353 err:
354         omapfb_unlock(fbdev);
355
356         return r;
357 }
358
359 static int omapfb_get_color_key(struct fb_info *fbi,
360                 struct omapfb_color_key *ck)
361 {
362         struct omapfb_info *ofbi = FB2OFB(fbi);
363         struct omapfb2_device *fbdev = ofbi->fbdev;
364         struct omap_overlay_manager *mgr = NULL;
365         int r = 0;
366         int i;
367
368         omapfb_lock(fbdev);
369
370         for (i = 0; i < ofbi->num_overlays; i++) {
371                 if (ofbi->overlays[i]->manager) {
372                         mgr = ofbi->overlays[i]->manager;
373                         break;
374                 }
375         }
376
377         if (!mgr) {
378                 r = -EINVAL;
379                 goto err;
380         }
381
382         if (!mgr->set_default_color ||
383                         !mgr->set_trans_key_type_and_value ||
384                         !mgr->enable_trans_key) {
385                 r = -ENODEV;
386                 goto err;
387         }
388
389         *ck = omapfb_color_keys[mgr->id];
390 err:
391         omapfb_unlock(fbdev);
392
393         return r;
394 }
395
396 static int omapfb_memory_read(struct fb_info *fbi,
397                 struct omapfb_memory_read *mr)
398 {
399         struct omap_display *display = fb2display(fbi);
400         struct omapfb_info *ofbi = FB2OFB(fbi);
401         struct omapfb2_device *fbdev = ofbi->fbdev;
402         void *buf;
403         int r;
404
405         if (!display || !display->memory_read)
406                 return -ENOENT;
407
408         if (!access_ok(VERIFY_WRITE, mr->buffer, mr->buffer_size))
409                 return -EFAULT;
410
411         if (mr->w * mr->h * 3 > mr->buffer_size)
412                 return -EINVAL;
413
414         buf = vmalloc(mr->buffer_size);
415         if (!buf) {
416                 DBG("vmalloc failed\n");
417                 return -ENOMEM;
418         }
419
420         omapfb_lock(fbdev);
421
422         r = display->memory_read(display, buf, mr->buffer_size,
423                         mr->x, mr->y, mr->w, mr->h);
424
425         if (r > 0) {
426                 if (copy_to_user(mr->buffer, buf, mr->buffer_size))
427                         r = -EFAULT;
428         }
429
430         vfree(buf);
431
432         omapfb_unlock(fbdev);
433
434         return r;
435 }
436
437 int omapfb_ioctl(struct fb_info *fbi, unsigned int cmd, unsigned long arg)
438 {
439         struct omapfb_info *ofbi = FB2OFB(fbi);
440         struct omapfb2_device *fbdev = ofbi->fbdev;
441         struct omap_display *display = fb2display(fbi);
442
443         union {
444                 struct omapfb_update_window_old uwnd_o;
445                 struct omapfb_update_window     uwnd;
446                 struct omapfb_plane_info        plane_info;
447                 struct omapfb_caps              caps;
448                 struct omapfb_mem_info          mem_info;
449                 struct omapfb_color_key         color_key;
450                 enum omapfb_update_mode         update_mode;
451                 int test_num;
452                 struct omapfb_memory_read       memory_read;
453                 u_int32_t                       crt;
454         } p;
455
456         int r = 0;
457
458         switch (cmd) {
459         case OMAPFB_SYNC_GFX:
460                 DBG("ioctl SYNC_GFX\n");
461                 if (!display || !display->sync) {
462                         /* DSS1 never returns an error here, so we neither */
463                         /*r = -EINVAL;*/
464                         break;
465                 }
466
467                 omapfb_lock(fbdev);
468                 r = display->sync(display);
469                 omapfb_unlock(fbdev);
470                 break;
471
472         case OMAPFB_UPDATE_WINDOW_OLD:
473                 DBG("ioctl UPDATE_WINDOW_OLD\n");
474                 if (!display || !display->update) {
475                         r = -EINVAL;
476                         break;
477                 }
478
479                 if (copy_from_user(&p.uwnd_o,
480                                         (void __user *)arg,
481                                         sizeof(p.uwnd_o))) {
482                         r = -EFAULT;
483                         break;
484                 }
485
486                 r = omapfb_update_window(fbi, p.uwnd_o.x, p.uwnd_o.y,
487                                 p.uwnd_o.width, p.uwnd_o.height);
488                 break;
489
490         case OMAPFB_UPDATE_WINDOW:
491                 DBG("ioctl UPDATE_WINDOW\n");
492                 if (!display || !display->update) {
493                         r = -EINVAL;
494                         break;
495                 }
496
497                 if (copy_from_user(&p.uwnd, (void __user *)arg,
498                                         sizeof(p.uwnd))) {
499                         r = -EFAULT;
500                         break;
501                 }
502
503                 r = omapfb_update_window(fbi, p.uwnd.x, p.uwnd.y,
504                                 p.uwnd.width, p.uwnd.height);
505                 break;
506
507         case OMAPFB_SETUP_PLANE:
508                 DBG("ioctl SETUP_PLANE\n");
509                 if (copy_from_user(&p.plane_info, (void __user *)arg,
510                                         sizeof(p.plane_info)))
511                         r = -EFAULT;
512                 else
513                         r = omapfb_setup_plane(fbi, &p.plane_info);
514                 break;
515
516         case OMAPFB_QUERY_PLANE:
517                 DBG("ioctl QUERY_PLANE\n");
518                 r = omapfb_query_plane(fbi, &p.plane_info);
519                 if (r < 0)
520                         break;
521                 if (copy_to_user((void __user *)arg, &p.plane_info,
522                                         sizeof(p.plane_info)))
523                         r = -EFAULT;
524                 break;
525
526         case OMAPFB_SETUP_MEM:
527                 DBG("ioctl SETUP_MEM\n");
528                 if (copy_from_user(&p.mem_info, (void __user *)arg,
529                                         sizeof(p.mem_info)))
530                         r = -EFAULT;
531                 else
532                         r = omapfb_setup_mem(fbi, &p.mem_info);
533                 break;
534
535         case OMAPFB_QUERY_MEM:
536                 DBG("ioctl QUERY_MEM\n");
537                 r = omapfb_query_mem(fbi, &p.mem_info);
538                 if (r < 0)
539                         break;
540                 if (copy_to_user((void __user *)arg, &p.mem_info,
541                                         sizeof(p.mem_info)))
542                         r = -EFAULT;
543                 break;
544
545         case OMAPFB_GET_CAPS:
546                 DBG("ioctl GET_CAPS\n");
547                 if (!display) {
548                         r = -EINVAL;
549                         break;
550                 }
551
552                 p.caps.ctrl = display->caps;
553
554                 if (copy_to_user((void __user *)arg, &p.caps, sizeof(p.caps)))
555                         r = -EFAULT;
556                 break;
557
558         case OMAPFB_SET_UPDATE_MODE:
559                 DBG("ioctl SET_UPDATE_MODE\n");
560                 if (get_user(p.update_mode, (int __user *)arg))
561                         r = -EFAULT;
562                 else
563                         r = omapfb_set_update_mode(fbi, p.update_mode);
564                 break;
565
566         case OMAPFB_GET_UPDATE_MODE:
567                 DBG("ioctl GET_UPDATE_MODE\n");
568                 r = omapfb_get_update_mode(fbi, &p.update_mode);
569                 if (r)
570                         break;
571                 if (put_user(p.update_mode,
572                                         (enum omapfb_update_mode __user *)arg))
573                         r = -EFAULT;
574                 break;
575
576         case OMAPFB_SET_COLOR_KEY:
577                 DBG("ioctl SET_COLOR_KEY\n");
578                 if (copy_from_user(&p.color_key, (void __user *)arg,
579                                    sizeof(p.color_key)))
580                         r = -EFAULT;
581                 else
582                         r = omapfb_set_color_key(fbi, &p.color_key);
583                 break;
584
585         case OMAPFB_GET_COLOR_KEY:
586                 DBG("ioctl GET_COLOR_KEY\n");
587                 if ((r = omapfb_get_color_key(fbi, &p.color_key)) < 0)
588                         break;
589                 if (copy_to_user((void __user *)arg, &p.color_key,
590                                  sizeof(p.color_key)))
591                         r = -EFAULT;
592                 break;
593
594         case FBIO_WAITFORVSYNC:
595                 if (get_user(p.crt, (u_int32_t __user *)arg)) {
596                         r = -EFAULT;
597                         break;
598                 }
599                 if (p.crt != 0) {
600                         r = -ENODEV;
601                         break;
602                 }
603                 /* FALLTHROUGH */
604
605         case OMAPFB_WAITFORVSYNC:
606                 DBG("ioctl WAITFORVSYNC\n");
607                 if (!display) {
608                         r = -EINVAL;
609                         break;
610                 }
611
612                 r = display->wait_vsync(display);
613                 break;
614
615         /* LCD and CTRL tests do the same thing for backward
616          * compatibility */
617         case OMAPFB_LCD_TEST:
618                 DBG("ioctl LCD_TEST\n");
619                 if (get_user(p.test_num, (int __user *)arg)) {
620                         r = -EFAULT;
621                         break;
622                 }
623                 if (!display || !display->run_test) {
624                         r = -EINVAL;
625                         break;
626                 }
627
628                 r = display->run_test(display, p.test_num);
629
630                 break;
631
632         case OMAPFB_CTRL_TEST:
633                 DBG("ioctl CTRL_TEST\n");
634                 if (get_user(p.test_num, (int __user *)arg)) {
635                         r = -EFAULT;
636                         break;
637                 }
638                 if (!display || !display->run_test) {
639                         r = -EINVAL;
640                         break;
641                 }
642
643                 r = display->run_test(display, p.test_num);
644
645                 break;
646
647         case OMAPFB_MEMORY_READ:
648                 DBG("ioctl MEMORY_READ\n");
649
650                 if (copy_from_user(&p.memory_read, (void __user *)arg,
651                                         sizeof(p.memory_read))) {
652                         r = -EFAULT;
653                         break;
654                 }
655
656                 r = omapfb_memory_read(fbi, &p.memory_read);
657
658                 break;
659
660         default:
661                 dev_err(fbdev->dev, "Unknown ioctl 0x%x\n", cmd);
662                 r = -EINVAL;
663         }
664
665         if (r < 0)
666                 DBG("ioctl failed: %d\n", r);
667
668         return r;
669 }
670
671