1 // SPDX-License-Identifier: GPL-2.0-only
3 * linux/drivers/video/omap2/omapfb-ioctl.c
5 * Copyright (C) 2008 Nokia Corporation
6 * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com>
8 * Some code and ideas taken from drivers/video/omap/ driver
13 #include <linux/device.h>
14 #include <linux/uaccess.h>
15 #include <linux/platform_device.h>
17 #include <linux/omapfb.h>
18 #include <linux/vmalloc.h>
19 #include <linux/export.h>
20 #include <linux/sizes.h>
22 #include <video/omapfb_dss.h>
23 #include <video/omapvrfb.h>
27 static u8
get_mem_idx(struct omapfb_info
*ofbi
)
29 if (ofbi
->id
== ofbi
->region
->id
)
32 return OMAPFB_MEM_IDX_ENABLED
| ofbi
->region
->id
;
35 static struct omapfb2_mem_region
*get_mem_region(struct omapfb_info
*ofbi
,
38 struct omapfb2_device
*fbdev
= ofbi
->fbdev
;
40 if (mem_idx
& OMAPFB_MEM_IDX_ENABLED
)
41 mem_idx
&= OMAPFB_MEM_IDX_MASK
;
45 if (mem_idx
>= fbdev
->num_fbs
)
48 return &fbdev
->regions
[mem_idx
];
51 static int omapfb_setup_plane(struct fb_info
*fbi
, struct omapfb_plane_info
*pi
)
53 struct omapfb_info
*ofbi
= FB2OFB(fbi
);
54 struct omapfb2_device
*fbdev
= ofbi
->fbdev
;
55 struct omap_overlay
*ovl
;
56 struct omap_overlay_info old_info
;
57 struct omapfb2_mem_region
*old_rg
, *new_rg
;
60 DBG("omapfb_setup_plane\n");
62 if (ofbi
->num_overlays
== 0) {
67 /* XXX uses only the first overlay */
68 ovl
= ofbi
->overlays
[0];
70 old_rg
= ofbi
->region
;
71 new_rg
= get_mem_region(ofbi
, pi
->mem_idx
);
77 /* Take the locks in a specific order to keep lockdep happy */
78 if (old_rg
->id
< new_rg
->id
) {
79 omapfb_get_mem_region(old_rg
);
80 omapfb_get_mem_region(new_rg
);
81 } else if (new_rg
->id
< old_rg
->id
) {
82 omapfb_get_mem_region(new_rg
);
83 omapfb_get_mem_region(old_rg
);
85 omapfb_get_mem_region(old_rg
);
87 if (pi
->enabled
&& !new_rg
->size
) {
89 * This plane's memory was freed, can't enable it
90 * until it's reallocated.
96 ovl
->get_overlay_info(ovl
, &old_info
);
98 if (old_rg
!= new_rg
) {
99 ofbi
->region
= new_rg
;
104 r
= ovl
->disable(ovl
);
110 r
= omapfb_setup_overlay(fbi
, ovl
, pi
->pos_x
, pi
->pos_y
,
111 pi
->out_width
, pi
->out_height
);
115 struct omap_overlay_info info
;
117 ovl
->get_overlay_info(ovl
, &info
);
119 info
.pos_x
= pi
->pos_x
;
120 info
.pos_y
= pi
->pos_y
;
121 info
.out_width
= pi
->out_width
;
122 info
.out_height
= pi
->out_height
;
124 r
= ovl
->set_overlay_info(ovl
, &info
);
130 r
= ovl
->manager
->apply(ovl
->manager
);
136 r
= ovl
->enable(ovl
);
141 /* Release the locks in a specific order to keep lockdep happy */
142 if (old_rg
->id
> new_rg
->id
) {
143 omapfb_put_mem_region(old_rg
);
144 omapfb_put_mem_region(new_rg
);
145 } else if (new_rg
->id
> old_rg
->id
) {
146 omapfb_put_mem_region(new_rg
);
147 omapfb_put_mem_region(old_rg
);
149 omapfb_put_mem_region(old_rg
);
154 if (old_rg
!= new_rg
) {
155 ofbi
->region
= old_rg
;
159 ovl
->set_overlay_info(ovl
, &old_info
);
161 /* Release the locks in a specific order to keep lockdep happy */
162 if (old_rg
->id
> new_rg
->id
) {
163 omapfb_put_mem_region(old_rg
);
164 omapfb_put_mem_region(new_rg
);
165 } else if (new_rg
->id
> old_rg
->id
) {
166 omapfb_put_mem_region(new_rg
);
167 omapfb_put_mem_region(old_rg
);
169 omapfb_put_mem_region(old_rg
);
171 dev_err(fbdev
->dev
, "setup_plane failed\n");
176 static int omapfb_query_plane(struct fb_info
*fbi
, struct omapfb_plane_info
*pi
)
178 struct omapfb_info
*ofbi
= FB2OFB(fbi
);
180 if (ofbi
->num_overlays
== 0) {
181 memset(pi
, 0, sizeof(*pi
));
183 struct omap_overlay
*ovl
;
184 struct omap_overlay_info ovli
;
186 ovl
= ofbi
->overlays
[0];
187 ovl
->get_overlay_info(ovl
, &ovli
);
189 pi
->pos_x
= ovli
.pos_x
;
190 pi
->pos_y
= ovli
.pos_y
;
191 pi
->enabled
= ovl
->is_enabled(ovl
);
192 pi
->channel_out
= 0; /* xxx */
194 pi
->mem_idx
= get_mem_idx(ofbi
);
195 pi
->out_width
= ovli
.out_width
;
196 pi
->out_height
= ovli
.out_height
;
202 static int omapfb_setup_mem(struct fb_info
*fbi
, struct omapfb_mem_info
*mi
)
204 struct omapfb_info
*ofbi
= FB2OFB(fbi
);
205 struct omapfb2_device
*fbdev
= ofbi
->fbdev
;
206 struct omap_dss_device
*display
= fb2display(fbi
);
207 struct omapfb2_mem_region
*rg
;
211 if (mi
->type
!= OMAPFB_MEMTYPE_SDRAM
)
214 size
= PAGE_ALIGN(mi
->size
);
216 if (display
&& display
->driver
->sync
)
217 display
->driver
->sync(display
);
221 down_write_nested(&rg
->lock
, rg
->id
);
222 atomic_inc(&rg
->lock_count
);
224 if (rg
->size
== size
&& rg
->type
== mi
->type
)
227 if (atomic_read(&rg
->map_count
)) {
232 for (i
= 0; i
< fbdev
->num_fbs
; i
++) {
233 struct omapfb_info
*ofbi2
= FB2OFB(fbdev
->fbs
[i
]);
236 if (ofbi2
->region
!= rg
)
239 for (j
= 0; j
< ofbi2
->num_overlays
; j
++) {
240 struct omap_overlay
*ovl
;
241 ovl
= ofbi2
->overlays
[j
];
242 if (ovl
->is_enabled(ovl
)) {
249 r
= omapfb_realloc_fbmem(fbi
, size
, mi
->type
);
251 dev_err(fbdev
->dev
, "realloc fbmem failed\n");
256 atomic_dec(&rg
->lock_count
);
262 static int omapfb_query_mem(struct fb_info
*fbi
, struct omapfb_mem_info
*mi
)
264 struct omapfb_info
*ofbi
= FB2OFB(fbi
);
265 struct omapfb2_mem_region
*rg
;
267 rg
= omapfb_get_mem_region(ofbi
->region
);
268 memset(mi
, 0, sizeof(*mi
));
273 omapfb_put_mem_region(rg
);
278 static int omapfb_update_window(struct fb_info
*fbi
,
279 u32 x
, u32 y
, u32 w
, u32 h
)
281 struct omap_dss_device
*display
= fb2display(fbi
);
287 if (w
== 0 || h
== 0)
290 display
->driver
->get_resolution(display
, &dw
, &dh
);
292 if (x
+ w
> dw
|| y
+ h
> dh
)
295 return display
->driver
->update(display
, x
, y
, w
, h
);
298 int omapfb_set_update_mode(struct fb_info
*fbi
,
299 enum omapfb_update_mode mode
)
301 struct omap_dss_device
*display
= fb2display(fbi
);
302 struct omapfb_info
*ofbi
= FB2OFB(fbi
);
303 struct omapfb2_device
*fbdev
= ofbi
->fbdev
;
304 struct omapfb_display_data
*d
;
310 if (mode
!= OMAPFB_AUTO_UPDATE
&& mode
!= OMAPFB_MANUAL_UPDATE
)
315 d
= get_display_data(fbdev
, display
);
317 if (d
->update_mode
== mode
) {
318 omapfb_unlock(fbdev
);
324 if (display
->caps
& OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE
) {
325 if (mode
== OMAPFB_AUTO_UPDATE
)
326 omapfb_start_auto_update(fbdev
, display
);
327 else /* MANUAL_UPDATE */
328 omapfb_stop_auto_update(fbdev
, display
);
330 d
->update_mode
= mode
;
331 } else { /* AUTO_UPDATE */
332 if (mode
== OMAPFB_MANUAL_UPDATE
)
336 omapfb_unlock(fbdev
);
341 int omapfb_get_update_mode(struct fb_info
*fbi
,
342 enum omapfb_update_mode
*mode
)
344 struct omap_dss_device
*display
= fb2display(fbi
);
345 struct omapfb_info
*ofbi
= FB2OFB(fbi
);
346 struct omapfb2_device
*fbdev
= ofbi
->fbdev
;
347 struct omapfb_display_data
*d
;
354 d
= get_display_data(fbdev
, display
);
356 *mode
= d
->update_mode
;
358 omapfb_unlock(fbdev
);
363 /* XXX this color key handling is a hack... */
364 static struct omapfb_color_key omapfb_color_keys
[2];
366 static int _omapfb_set_color_key(struct omap_overlay_manager
*mgr
,
367 struct omapfb_color_key
*ck
)
369 struct omap_overlay_manager_info info
;
370 enum omap_dss_trans_key_type kt
;
373 mgr
->get_manager_info(mgr
, &info
);
375 if (ck
->key_type
== OMAPFB_COLOR_KEY_DISABLED
) {
376 info
.trans_enabled
= false;
377 omapfb_color_keys
[mgr
->id
] = *ck
;
379 r
= mgr
->set_manager_info(mgr
, &info
);
388 switch (ck
->key_type
) {
389 case OMAPFB_COLOR_KEY_GFX_DST
:
390 kt
= OMAP_DSS_COLOR_KEY_GFX_DST
;
392 case OMAPFB_COLOR_KEY_VID_SRC
:
393 kt
= OMAP_DSS_COLOR_KEY_VID_SRC
;
399 info
.default_color
= ck
->background
;
400 info
.trans_key
= ck
->trans_key
;
401 info
.trans_key_type
= kt
;
402 info
.trans_enabled
= true;
404 omapfb_color_keys
[mgr
->id
] = *ck
;
406 r
= mgr
->set_manager_info(mgr
, &info
);
415 static int omapfb_set_color_key(struct fb_info
*fbi
,
416 struct omapfb_color_key
*ck
)
418 struct omapfb_info
*ofbi
= FB2OFB(fbi
);
419 struct omapfb2_device
*fbdev
= ofbi
->fbdev
;
422 struct omap_overlay_manager
*mgr
= NULL
;
426 for (i
= 0; i
< ofbi
->num_overlays
; i
++) {
427 if (ofbi
->overlays
[i
]->manager
) {
428 mgr
= ofbi
->overlays
[i
]->manager
;
438 r
= _omapfb_set_color_key(mgr
, ck
);
440 omapfb_unlock(fbdev
);
445 static int omapfb_get_color_key(struct fb_info
*fbi
,
446 struct omapfb_color_key
*ck
)
448 struct omapfb_info
*ofbi
= FB2OFB(fbi
);
449 struct omapfb2_device
*fbdev
= ofbi
->fbdev
;
450 struct omap_overlay_manager
*mgr
= NULL
;
456 for (i
= 0; i
< ofbi
->num_overlays
; i
++) {
457 if (ofbi
->overlays
[i
]->manager
) {
458 mgr
= ofbi
->overlays
[i
]->manager
;
468 *ck
= omapfb_color_keys
[mgr
->id
];
470 omapfb_unlock(fbdev
);
475 static int omapfb_memory_read(struct fb_info
*fbi
,
476 struct omapfb_memory_read
*mr
)
478 struct omap_dss_device
*display
= fb2display(fbi
);
482 if (!display
|| !display
->driver
->memory_read
)
485 if (mr
->w
> 4096 || mr
->h
> 4096)
488 if (mr
->w
* mr
->h
* 3 > mr
->buffer_size
)
491 buf
= vmalloc(mr
->buffer_size
);
493 DBG("vmalloc failed\n");
497 r
= display
->driver
->memory_read(display
, buf
, mr
->buffer_size
,
498 mr
->x
, mr
->y
, mr
->w
, mr
->h
);
501 if (copy_to_user(mr
->buffer
, buf
, r
))
510 static int omapfb_get_ovl_colormode(struct omapfb2_device
*fbdev
,
511 struct omapfb_ovl_colormode
*mode
)
513 int ovl_idx
= mode
->overlay_idx
;
514 int mode_idx
= mode
->mode_idx
;
515 struct omap_overlay
*ovl
;
516 enum omap_color_mode supported_modes
;
517 struct fb_var_screeninfo var
;
520 if (ovl_idx
>= fbdev
->num_overlays
)
522 ovl
= fbdev
->overlays
[ovl_idx
];
523 supported_modes
= ovl
->supported_modes
;
525 mode_idx
= mode
->mode_idx
;
527 for (i
= 0; i
< sizeof(supported_modes
) * 8; i
++) {
528 if (!(supported_modes
& (1 << i
)))
531 * It's possible that the FB doesn't support a mode
532 * that is supported by the overlay, so call the
535 if (dss_mode_to_fb_mode(1 << i
, &var
) < 0)
543 if (i
== sizeof(supported_modes
) * 8)
546 mode
->bits_per_pixel
= var
.bits_per_pixel
;
547 mode
->nonstd
= var
.nonstd
;
549 mode
->green
= var
.green
;
550 mode
->blue
= var
.blue
;
551 mode
->transp
= var
.transp
;
556 static int omapfb_wait_for_go(struct fb_info
*fbi
)
558 struct omapfb_info
*ofbi
= FB2OFB(fbi
);
562 for (i
= 0; i
< ofbi
->num_overlays
; ++i
) {
563 struct omap_overlay
*ovl
= ofbi
->overlays
[i
];
564 r
= ovl
->wait_for_go(ovl
);
572 int omapfb_ioctl(struct fb_info
*fbi
, unsigned int cmd
, unsigned long arg
)
574 struct omapfb_info
*ofbi
= FB2OFB(fbi
);
575 struct omapfb2_device
*fbdev
= ofbi
->fbdev
;
576 struct omap_dss_device
*display
= fb2display(fbi
);
577 struct omap_overlay_manager
*mgr
;
580 struct omapfb_update_window_old uwnd_o
;
581 struct omapfb_update_window uwnd
;
582 struct omapfb_plane_info plane_info
;
583 struct omapfb_caps caps
;
584 struct omapfb_mem_info mem_info
;
585 struct omapfb_color_key color_key
;
586 struct omapfb_ovl_colormode ovl_colormode
;
587 enum omapfb_update_mode update_mode
;
589 struct omapfb_memory_read memory_read
;
590 struct omapfb_vram_info vram_info
;
591 struct omapfb_tearsync_info tearsync_info
;
592 struct omapfb_display_info display_info
;
598 memset(&p
, 0, sizeof(p
));
601 case OMAPFB_SYNC_GFX
:
602 DBG("ioctl SYNC_GFX\n");
603 if (!display
|| !display
->driver
->sync
) {
604 /* DSS1 never returns an error here, so we neither */
609 r
= display
->driver
->sync(display
);
612 case OMAPFB_UPDATE_WINDOW_OLD
:
613 DBG("ioctl UPDATE_WINDOW_OLD\n");
614 if (!display
|| !display
->driver
->update
) {
619 if (copy_from_user(&p
.uwnd_o
,
626 r
= omapfb_update_window(fbi
, p
.uwnd_o
.x
, p
.uwnd_o
.y
,
627 p
.uwnd_o
.width
, p
.uwnd_o
.height
);
630 case OMAPFB_UPDATE_WINDOW
:
631 DBG("ioctl UPDATE_WINDOW\n");
632 if (!display
|| !display
->driver
->update
) {
637 if (copy_from_user(&p
.uwnd
, (void __user
*)arg
,
643 r
= omapfb_update_window(fbi
, p
.uwnd
.x
, p
.uwnd
.y
,
644 p
.uwnd
.width
, p
.uwnd
.height
);
647 case OMAPFB_SETUP_PLANE
:
648 DBG("ioctl SETUP_PLANE\n");
649 if (copy_from_user(&p
.plane_info
, (void __user
*)arg
,
650 sizeof(p
.plane_info
)))
653 r
= omapfb_setup_plane(fbi
, &p
.plane_info
);
656 case OMAPFB_QUERY_PLANE
:
657 DBG("ioctl QUERY_PLANE\n");
658 r
= omapfb_query_plane(fbi
, &p
.plane_info
);
661 if (copy_to_user((void __user
*)arg
, &p
.plane_info
,
662 sizeof(p
.plane_info
)))
666 case OMAPFB_SETUP_MEM
:
667 DBG("ioctl SETUP_MEM\n");
668 if (copy_from_user(&p
.mem_info
, (void __user
*)arg
,
672 r
= omapfb_setup_mem(fbi
, &p
.mem_info
);
675 case OMAPFB_QUERY_MEM
:
676 DBG("ioctl QUERY_MEM\n");
677 r
= omapfb_query_mem(fbi
, &p
.mem_info
);
680 if (copy_to_user((void __user
*)arg
, &p
.mem_info
,
685 case OMAPFB_GET_CAPS
:
686 DBG("ioctl GET_CAPS\n");
692 memset(&p
.caps
, 0, sizeof(p
.caps
));
693 if (display
->caps
& OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE
)
694 p
.caps
.ctrl
|= OMAPFB_CAPS_MANUAL_UPDATE
;
695 if (display
->caps
& OMAP_DSS_DISPLAY_CAP_TEAR_ELIM
)
696 p
.caps
.ctrl
|= OMAPFB_CAPS_TEARSYNC
;
698 if (copy_to_user((void __user
*)arg
, &p
.caps
, sizeof(p
.caps
)))
702 case OMAPFB_GET_OVERLAY_COLORMODE
:
703 DBG("ioctl GET_OVERLAY_COLORMODE\n");
704 if (copy_from_user(&p
.ovl_colormode
, (void __user
*)arg
,
705 sizeof(p
.ovl_colormode
))) {
709 r
= omapfb_get_ovl_colormode(fbdev
, &p
.ovl_colormode
);
712 if (copy_to_user((void __user
*)arg
, &p
.ovl_colormode
,
713 sizeof(p
.ovl_colormode
)))
717 case OMAPFB_SET_UPDATE_MODE
:
718 DBG("ioctl SET_UPDATE_MODE\n");
719 if (get_user(p
.update_mode
, (int __user
*)arg
))
722 r
= omapfb_set_update_mode(fbi
, p
.update_mode
);
725 case OMAPFB_GET_UPDATE_MODE
:
726 DBG("ioctl GET_UPDATE_MODE\n");
727 r
= omapfb_get_update_mode(fbi
, &p
.update_mode
);
730 if (put_user(p
.update_mode
,
731 (enum omapfb_update_mode __user
*)arg
))
735 case OMAPFB_SET_COLOR_KEY
:
736 DBG("ioctl SET_COLOR_KEY\n");
737 if (copy_from_user(&p
.color_key
, (void __user
*)arg
,
738 sizeof(p
.color_key
)))
741 r
= omapfb_set_color_key(fbi
, &p
.color_key
);
744 case OMAPFB_GET_COLOR_KEY
:
745 DBG("ioctl GET_COLOR_KEY\n");
746 r
= omapfb_get_color_key(fbi
, &p
.color_key
);
749 if (copy_to_user((void __user
*)arg
, &p
.color_key
,
750 sizeof(p
.color_key
)))
754 case FBIO_WAITFORVSYNC
:
755 if (get_user(p
.crt
, (__u32 __user
*)arg
)) {
765 case OMAPFB_WAITFORVSYNC
:
766 DBG("ioctl WAITFORVSYNC\n");
773 mgr
= omapdss_find_mgr_from_display(display
);
779 r
= mgr
->wait_for_vsync(mgr
);
782 case OMAPFB_WAITFORGO
:
783 DBG("ioctl WAITFORGO\n");
789 r
= omapfb_wait_for_go(fbi
);
792 /* LCD and CTRL tests do the same thing for backward
794 case OMAPFB_LCD_TEST
:
795 DBG("ioctl LCD_TEST\n");
796 if (get_user(p
.test_num
, (int __user
*)arg
)) {
800 if (!display
|| !display
->driver
->run_test
) {
805 r
= display
->driver
->run_test(display
, p
.test_num
);
809 case OMAPFB_CTRL_TEST
:
810 DBG("ioctl CTRL_TEST\n");
811 if (get_user(p
.test_num
, (int __user
*)arg
)) {
815 if (!display
|| !display
->driver
->run_test
) {
820 r
= display
->driver
->run_test(display
, p
.test_num
);
824 case OMAPFB_MEMORY_READ
:
825 DBG("ioctl MEMORY_READ\n");
827 if (copy_from_user(&p
.memory_read
, (void __user
*)arg
,
828 sizeof(p
.memory_read
))) {
833 r
= omapfb_memory_read(fbi
, &p
.memory_read
);
837 case OMAPFB_GET_VRAM_INFO
: {
838 DBG("ioctl GET_VRAM_INFO\n");
841 * We don't have the ability to get this vram info anymore.
842 * Fill in something that should keep the applications working.
844 p
.vram_info
.total
= SZ_1M
* 64;
845 p
.vram_info
.free
= SZ_1M
* 64;
846 p
.vram_info
.largest_free_block
= SZ_1M
* 64;
848 if (copy_to_user((void __user
*)arg
, &p
.vram_info
,
849 sizeof(p
.vram_info
)))
854 case OMAPFB_SET_TEARSYNC
: {
855 DBG("ioctl SET_TEARSYNC\n");
857 if (copy_from_user(&p
.tearsync_info
, (void __user
*)arg
,
858 sizeof(p
.tearsync_info
))) {
863 if (!display
|| !display
->driver
->enable_te
) {
868 r
= display
->driver
->enable_te(display
,
869 !!p
.tearsync_info
.enabled
);
874 case OMAPFB_GET_DISPLAY_INFO
: {
877 DBG("ioctl GET_DISPLAY_INFO\n");
879 if (display
== NULL
) {
884 display
->driver
->get_resolution(display
, &xres
, &yres
);
886 p
.display_info
.xres
= xres
;
887 p
.display_info
.yres
= yres
;
889 if (display
->driver
->get_dimensions
) {
891 display
->driver
->get_dimensions(display
, &w
, &h
);
892 p
.display_info
.width
= w
;
893 p
.display_info
.height
= h
;
895 p
.display_info
.width
= 0;
896 p
.display_info
.height
= 0;
899 if (copy_to_user((void __user
*)arg
, &p
.display_info
,
900 sizeof(p
.display_info
)))
906 dev_err(fbdev
->dev
, "Unknown ioctl 0x%x\n", cmd
);
911 DBG("ioctl failed: %d\n", r
);