add prep tool and common
[ginge.git] / common / fbdev.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <sys/types.h>
5 #include <sys/stat.h>
6 #include <fcntl.h>
7 #include <sys/ioctl.h>
8 #include <sys/mman.h>
9 #include <unistd.h>
10 #include <linux/fb.h>
11 #include <linux/matroxfb.h>
12
13 #include "fbdev.h"
14
15 #define FBDEV_MAX_BUFFERS 3
16
17 struct vout_fbdev {
18         int     fd;
19         void    *mem;
20         size_t  mem_size;
21         struct  fb_var_screeninfo fbvar_old;
22         struct  fb_var_screeninfo fbvar_new;
23         int     buffer_write;
24         void    *buffers[FBDEV_MAX_BUFFERS];
25         int     buffer_count;
26 };
27
28 void *vout_fbdev_flip(struct vout_fbdev *fbdev)
29 {
30         int draw_buf;
31
32         if (fbdev->buffer_count < 2)
33                 return fbdev->mem;
34
35         draw_buf = fbdev->buffer_write;
36         fbdev->buffer_write++;
37         if (fbdev->buffer_write >= fbdev->buffer_count)
38                 fbdev->buffer_write = 0;
39
40         fbdev->fbvar_new.yoffset = fbdev->fbvar_old.yres * draw_buf;
41
42         ioctl(fbdev->fd, FBIOPAN_DISPLAY, &fbdev->fbvar_new);
43
44         return fbdev->buffers[fbdev->buffer_write];
45 }
46
47 void vout_fbdev_wait_vsync(struct vout_fbdev *fbdev)
48 {
49         int arg = 0;
50         ioctl(fbdev->fd, FBIO_WAITFORVSYNC, &arg);
51 }
52
53 void vout_fbdev_clear(struct vout_fbdev *fbdev)
54 {
55         memset(fbdev->mem, 0, fbdev->mem_size);
56 }
57
58 struct vout_fbdev *vout_fbdev_init(const char *fbdev_name, int *w, int *h, int no_dblbuf)
59 {
60         struct vout_fbdev *fbdev;
61         int i, ret;
62
63         fbdev = calloc(1, sizeof(*fbdev));
64         if (fbdev == NULL)
65                 return NULL;
66
67         fbdev->fd = open(fbdev_name, O_RDWR, 0);
68         if (fbdev->fd == -1) {
69                 fprintf(stderr, "%s: ", fbdev_name);
70                 perror("open");
71                 goto fail_open;
72         }
73
74         ret = ioctl(fbdev->fd, FBIOGET_VSCREENINFO, &fbdev->fbvar_old);
75         if (ret == -1) {
76                 perror("FBIOGET_VSCREENINFO ioctl");
77                 goto fail;
78         }
79
80         fbdev->fbvar_new = fbdev->fbvar_old;
81         printf("%s: %ix%i@%d\n", fbdev_name, fbdev->fbvar_old.xres, fbdev->fbvar_old.yres,
82                 fbdev->fbvar_old.bits_per_pixel);
83         *w = fbdev->fbvar_old.xres;
84         *h = fbdev->fbvar_old.yres;
85         fbdev->buffer_count = FBDEV_MAX_BUFFERS; // be optimistic
86         if (no_dblbuf)
87                 fbdev->buffer_count = 1;
88
89         if (fbdev->fbvar_new.bits_per_pixel != 16) {
90                 printf(" switching to 16bpp\n");
91                 fbdev->fbvar_new.bits_per_pixel = 16;
92                 ret = ioctl(fbdev->fd, FBIOPUT_VSCREENINFO, &fbdev->fbvar_new);
93                 if (ret == -1) {
94                         perror("FBIOPUT_VSCREENINFO ioctl");
95                         goto fail;
96                 }
97         }
98
99         if (fbdev->fbvar_new.yres_virtual < fbdev->fbvar_old.yres * fbdev->buffer_count) {
100                 fbdev->fbvar_new.yres_virtual = fbdev->fbvar_old.yres * fbdev->buffer_count;
101                 ret = ioctl(fbdev->fd, FBIOPUT_VSCREENINFO, &fbdev->fbvar_new);
102                 if (ret == -1) {
103                         fbdev->buffer_count = 1;
104                         fprintf(stderr, "Warning: failed to increase virtual resolution, "
105                                         "doublebuffering disabled\n");
106                 }
107         }
108
109         fbdev->mem_size = *w * *h * 2 * fbdev->buffer_count;
110         fbdev->mem = mmap(0, fbdev->mem_size, PROT_WRITE|PROT_READ, MAP_SHARED, fbdev->fd, 0);
111         if (fbdev->mem == MAP_FAILED && fbdev->buffer_count > 1) {
112                 fprintf(stderr, "Warning: can't map %zd bytes, doublebuffering disabled\n", fbdev->mem_size);
113                 fbdev->mem_size = *w * *h * 2;
114                 fbdev->buffer_count = 1;
115                 fbdev->mem = mmap(0, fbdev->mem_size, PROT_WRITE|PROT_READ, MAP_SHARED, fbdev->fd, 0);
116         }
117         if (fbdev->mem == MAP_FAILED) {
118                 perror("mmap framebuffer");
119                 goto fail;
120         }
121         memset(fbdev->mem, 0, fbdev->mem_size);
122         for (i = 0; i < fbdev->buffer_count; i++)
123                 fbdev->buffers[i] = (char *)fbdev->mem + i * *w * *h * 2;
124
125         // some checks
126         ret = 0;
127         ret = ioctl(fbdev->fd, FBIO_WAITFORVSYNC, &ret);
128         if (ret != 0)
129                 fprintf(stderr, "Warning: vsync doesn't seem to be supported\n");
130
131         if (fbdev->buffer_count > 1) {
132                 fbdev->buffer_write = 0;
133                 fbdev->fbvar_new.yoffset = fbdev->fbvar_old.yres * (fbdev->buffer_count - 1);
134                 ret = ioctl(fbdev->fd, FBIOPAN_DISPLAY, &fbdev->fbvar_new);
135                 if (ret != 0) {
136                         fbdev->buffer_count = 1;
137                         fprintf(stderr, "Warning: can't pan display, doublebuffering disabled\n");
138                 }
139         }
140
141         printf("fbdev initialized.\n");
142         return fbdev;
143
144 fail:
145         close(fbdev->fd);
146 fail_open:
147         free(fbdev);
148         return NULL;
149 }
150
151 void vout_fbdev_finish(struct vout_fbdev *fbdev)
152 {
153         ioctl(fbdev->fd, FBIOPUT_VSCREENINFO, &fbdev->fbvar_old);
154         if (fbdev->mem != MAP_FAILED)
155                 munmap(fbdev->mem, fbdev->mem_size);
156         if (fbdev->fd >= 0)
157                 close(fbdev->fd);
158         fbdev->mem = NULL;
159         fbdev->fd = -1;
160         free(fbdev);
161 }
162
163 #if 0
164 void *g_screen_ptr;
165 int main()
166 {
167         int w, h;
168         vout_fbdev_init(&w, &h);
169         //while (1)
170         {
171                 memset(g_screen_ptr, 0xff, fbdev_mem_size / 2);
172                 plat_video_wait_vsync();
173                 plat_video_flip();
174                 memset(g_screen_ptr, 0x00, fbdev_mem_size / 2);
175                 usleep(8000);
176 //              plat_video_wait_vsync();
177                 plat_video_flip();
178         }
179 }
180 #endif