fc780010332d5cdf95e1618b4a4882f4e123b354
[sdl_omap.git] / osdl_video.c
1 /*
2  * (C) GraÅžvydas "notaz" Ignotas, 2010
3  *
4  * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
5  * See the COPYING file in the top-level directory.
6  */
7
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <string.h>
11 #include <strings.h>
12 #include <ctype.h>
13 #include <sys/types.h>
14 #include <sys/stat.h>
15 #include <fcntl.h>
16 #include <sys/ioctl.h>
17 #include <unistd.h>
18 #include <linux/fb.h>
19
20 #include "omapsdl.h"
21 #include "omapfb.h"
22 #include "linux/fbdev.h"
23 #include "linux/oshide.h"
24
25 struct omapfb_saved_layer {
26         struct omapfb_plane_info pi;
27         struct omapfb_mem_info mi;
28 };
29
30 static const char *get_fb_device(void)
31 {
32         const char *fbname = getenv("SDL_FBDEV");
33         if (fbname == NULL)
34                 fbname = "/dev/fb1";
35
36         return fbname;
37 }
38
39 static int osdl_setup_omapfb(int fd, int enabled, int x, int y, int w, int h, int mem)
40 {
41         struct omapfb_plane_info pi;
42         struct omapfb_mem_info mi;
43         int ret;
44
45         ret = ioctl(fd, OMAPFB_QUERY_PLANE, &pi);
46         if (ret != 0) {
47                 err_perror("QUERY_PLANE");
48                 return -1;
49         }
50
51         ret = ioctl(fd, OMAPFB_QUERY_MEM, &mi);
52         if (ret != 0) {
53                 err_perror("QUERY_MEM");
54                 return -1;
55         }
56
57         /* must disable when changing stuff */
58         if (pi.enabled) {
59                 pi.enabled = 0;
60                 ret = ioctl(fd, OMAPFB_SETUP_PLANE, &pi);
61                 if (ret != 0)
62                         err_perror("SETUP_PLANE");
63         }
64
65         mi.size = mem;
66         ret = ioctl(fd, OMAPFB_SETUP_MEM, &mi);
67         if (ret != 0) {
68                 err_perror("SETUP_MEM");
69                 return -1;
70         }
71
72         pi.pos_x = x;
73         pi.pos_y = y;
74         pi.out_width = w;
75         pi.out_height = h;
76         pi.enabled = enabled;
77
78         ret = ioctl(fd, OMAPFB_SETUP_PLANE, &pi);
79         if (ret != 0) {
80                 err_perror("SETUP_PLANE");
81                 err("(%d %d %d %d)\n", x, y, w, h);
82                 return -1;
83         }
84
85         return 0;
86 }
87
88 static int read_sysfs(const char *fname, char *buff, size_t size)
89 {
90         FILE *f;
91         int ret;
92
93         f = fopen(fname, "r");
94         if (f == NULL) {
95                 err_perror("open %s: ", fname);
96                 return -1;
97         }
98
99         ret = fread(buff, 1, size - 1, f);
100         fclose(f);
101         if (ret <= 0) {
102                 err_perror("read %s: ", fname);
103                 return -1;
104         }
105
106         buff[ret] = 0;
107         for (ret--; ret >= 0 && isspace(buff[ret]); ret--)
108                 buff[ret] = 0;
109
110         return 0;
111 }
112
113 int osdl_video_detect_screen(struct SDL_PrivateVideoData *pdata)
114 {
115         int fb_id, overlay_id = -1, screen_id = -1;
116         struct fb_var_screeninfo fbvar;
117         char buff[64], screen_name[64];
118         const char *fbname;
119         struct stat status;
120         int fd, i, ret;
121         int w, h;
122         FILE *f;
123
124         fbname = get_fb_device();
125
126         /* Figure out screen resolution, we need to know default resolution
127          * to report to SDL and for centering stuff.
128          * The only way to achieve this seems to be walking some sysfs files.. */
129         ret = stat(fbname, &status);
130         if (ret != 0) {
131                 err_perror("can't stat %s", fbname);
132                 goto skip_screen;
133         }
134         fb_id = minor(status.st_rdev);
135
136         snprintf(buff, sizeof(buff), "/sys/class/graphics/fb%d/overlays", fb_id);
137         f = fopen(buff, "r");
138         if (f == NULL) {
139                 err("can't open %s, skip screen detection", buff);
140                 goto skip_screen;
141         }
142
143         ret = fscanf(f, "%d", &overlay_id);
144         fclose(f);
145         if (ret != 1) {
146                 err("can't parse %s, skip screen detection", buff);
147                 goto skip_screen;
148         }
149
150         snprintf(buff, sizeof(buff), "/sys/devices/platform/omapdss/overlay%d/manager", overlay_id);
151         ret = read_sysfs(buff, screen_name, sizeof(screen_name));
152         if (ret < 0) {
153                 err("skip screen detection");
154                 goto skip_screen;
155         }
156
157         for (i = 0; ; i++) {
158                 snprintf(buff, sizeof(buff), "/sys/devices/platform/omapdss/display%d/name", i);
159                 ret = read_sysfs(buff, buff, sizeof(buff));
160                 if (ret < 0)
161                         break;
162
163                 if (strcmp(screen_name, buff) == 0) {
164                         screen_id = i;
165                         break;
166                 }
167         }
168
169         if (screen_id < 0) {
170                 err("could not find screen");
171                 goto skip_screen;
172         }
173
174         snprintf(buff, sizeof(buff), "/sys/devices/platform/omapdss/display%d/timings", screen_id);
175         f = fopen(buff, "r");
176         if (f == NULL) {
177                 err("can't open %s, skip screen detection", buff);
178                 goto skip_screen;
179         }
180
181         ret = fscanf(f, "%*d,%d/%*d/%*d/%*d,%d/%*d/%*d/%*d", &w, &h);
182         fclose(f);
183         if (ret != 2) {
184                 err("can't parse %s (%d), skip screen detection", buff, ret);
185                 goto skip_screen;
186         }
187
188         log("detected %dx%d '%s' (%d) screen attached to fb %d and overlay %d",
189                 w, h, screen_name, screen_id, fb_id, overlay_id);
190
191         pdata->screen_w = w;
192         pdata->screen_h = h;
193         return 0;
194
195 skip_screen:
196         /* attempt to extract this from FB then */
197         fd = open(fbname, O_RDWR);
198         if (fd == -1) {
199                 err_perror("open %s", fbname);
200                 return -1;
201         }
202
203         ret = ioctl(fd, FBIOGET_VSCREENINFO, &fbvar);
204         close(fd);
205         if (ret == -1) {
206                 err_perror("ioctl %s", fbname);
207                 return -1;
208         }
209
210         if (fbvar.xres == 0 || fbvar.yres == 0) {
211                 err("VSCREENINFO has nothing meaningful");
212                 return -1;
213         }
214
215         pdata->screen_w = fbvar.xres;
216         pdata->screen_h = fbvar.yres;
217         return 0;
218 }
219
220 static int osdl_setup_omap_layer(struct SDL_PrivateVideoData *pdata,
221                 const char *fbname, int width, int height, int bpp)
222 {
223         int x = 0, y = 0, w = width, h = height; /* layer size and pos */
224         int screen_w = w, screen_h = h;
225         const char *tmp;
226         int ret, fd;
227
228         if (pdata->screen_w != 0)
229                 screen_w = pdata->screen_w;
230         if (pdata->screen_h != 0)
231                 screen_h = pdata->screen_h;
232
233         fd = open(fbname, O_RDWR);
234         if (fd == -1) {
235                 err_perror("open %s", fbname);
236                 return -1;
237         }
238
239         /* FIXME: assuming layer doesn't change here */
240         if (pdata->saved_layer == NULL) {
241                 struct omapfb_saved_layer *slayer;
242                 slayer = calloc(1, sizeof(*slayer));
243                 if (slayer == NULL)
244                         return -1;
245
246                 ret = ioctl(fd, OMAPFB_QUERY_PLANE, &slayer->pi);
247                 if (ret != 0) {
248                         err_perror("QUERY_PLANE");
249                         return -1;
250                 }
251
252                 ret = ioctl(fd, OMAPFB_QUERY_MEM, &slayer->mi);
253                 if (ret != 0) {
254                         err_perror("QUERY_MEM");
255                         return -1;
256                 }
257
258                 pdata->saved_layer = slayer;
259         }
260
261         tmp = getenv("SDL_OMAP_LAYER_SIZE");
262         if (tmp != NULL) {
263                 int w_, h_;
264                 if (strcasecmp(tmp, "fullscreen") == 0)
265                         w = screen_w, h = screen_h;
266                 else if (sscanf(tmp, "%dx%d", &w_, &h_) == 2)
267                         w = w_, h = h_;
268                 else
269                         err("layer size specified incorrectly, "
270                                 "should be like 800x480");
271         }
272
273         x = screen_w / 2 - w / 2;
274         y = screen_h / 2 - h / 2;
275         ret = osdl_setup_omapfb(fd, 1, x, y, w, h, width * height * ((bpp + 7) / 8) * 3);
276         close(fd);
277
278         return ret;
279 }
280
281 int osdl_video_set_mode(struct SDL_PrivateVideoData *pdata, int width, int height, int bpp)
282 {
283         const char *fbname;
284         int ret;
285
286         bpp = 16; // FIXME
287
288         fbname = get_fb_device();
289
290         if (pdata->fbdev != NULL) {
291                 vout_fbdev_finish(pdata->fbdev);
292                 pdata->fbdev = NULL;
293         }
294
295         omapsdl_config_from_env();
296
297         ret = osdl_setup_omap_layer(pdata, fbname, width, height, bpp);
298         if (ret < 0)
299                 return -1;
300
301         pdata->fbdev = vout_fbdev_init(fbname, &width, &height, 0);
302         if (pdata->fbdev == NULL)
303                 return -1;
304
305         if (!pdata->oshide_done) {
306                 oshide_init();
307                 pdata->oshide_done = 1;
308         }
309
310         return 0;
311 }
312
313 void *osdl_video_flip(struct SDL_PrivateVideoData *pdata)
314 {
315         if (pdata->fbdev == NULL)
316                 return NULL;
317
318         if (gcfg_force_vsync)
319                 vout_fbdev_wait_vsync(pdata->fbdev);
320
321         return vout_fbdev_flip(pdata->fbdev);
322 }
323
324 void osdl_video_finish(struct SDL_PrivateVideoData *pdata)
325 {
326         static const char *fbname;
327
328         fbname = get_fb_device();
329         if (pdata->fbdev != NULL) {
330                 vout_fbdev_finish(pdata->fbdev);
331                 pdata->fbdev = NULL;
332         }
333
334         /* restore the OMAP layer */
335         if (pdata->saved_layer != NULL) {
336                 struct omapfb_saved_layer *slayer = pdata->saved_layer;
337                 int fd;
338
339                 fd = open(fbname, O_RDWR);
340                 if (fd != -1) {
341                         int enabled = slayer->pi.enabled;
342
343                         /* be sure to disable while setting up */
344                         slayer->pi.enabled = 0;
345                         ioctl(fd, OMAPFB_SETUP_PLANE, &slayer->pi);
346                         ioctl(fd, OMAPFB_SETUP_MEM, &slayer->mi);
347                         if (enabled) {
348                                 slayer->pi.enabled = enabled;
349                                 ioctl(fd, OMAPFB_SETUP_PLANE, &slayer->pi);
350                         }
351                         close(fd);
352                 }
353                 free(slayer);
354                 pdata->saved_layer = NULL;
355         }
356
357         if (pdata->oshide_done) {
358                 oshide_finish();
359                 pdata->oshide_done = 0;
360         }
361 }
362