limited minimize support
[sdl_omap.git] / src / video / omapdss / osdl_video.c
1 /*
2  * (C) GraÅžvydas "notaz" Ignotas, 2010-2012
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 "osdl.h"
21 #include "omapfb.h"
22 #include "linux/fbdev.h"
23 #include "linux/xenv.h"
24
25 struct omapfb_state {
26         struct omapfb_plane_info pi;
27         struct omapfb_mem_info mi;
28         struct omapfb_plane_info pi_old;
29         struct omapfb_mem_info mi_old;
30 };
31
32 static const char *get_fb_device(void)
33 {
34         const char *fbname = getenv("SDL_FBDEV");
35         if (fbname == NULL)
36                 fbname = "/dev/fb1";
37
38         return fbname;
39 }
40
41 static int osdl_setup_omapfb(struct omapfb_state *ostate, int fd, int enabled,
42         int x, int y, int w, int h, int mem, int *buffer_count)
43 {
44         struct omapfb_plane_info pi;
45         struct omapfb_mem_info mi;
46         int mem_blocks = *buffer_count;
47         int ret;
48
49         memset(&pi, 0, sizeof(pi));
50         memset(&mi, 0, sizeof(mi));
51
52         ret = ioctl(fd, OMAPFB_QUERY_PLANE, &pi);
53         if (ret != 0) {
54                 err_perror("QUERY_PLANE");
55                 return -1;
56         }
57
58         ret = ioctl(fd, OMAPFB_QUERY_MEM, &mi);
59         if (ret != 0) {
60                 err_perror("QUERY_MEM");
61                 return -1;
62         }
63
64         /* must disable when changing stuff */
65         if (pi.enabled) {
66                 pi.enabled = 0;
67                 ret = ioctl(fd, OMAPFB_SETUP_PLANE, &pi);
68                 if (ret != 0)
69                         err_perror("SETUP_PLANE");
70         }
71
72         if (mi.size < mem * mem_blocks) {
73                 for (; mem_blocks > 0; mem_blocks--) {
74                         mi.size = mem * mem_blocks;
75                         ret = ioctl(fd, OMAPFB_SETUP_MEM, &mi);
76                         if (ret == 0)
77                                 break;
78                 }
79                 if (ret != 0 || mem_blocks <= 0) {
80                         err("failed to allocate at least %d bytes of vram:\n", mem);
81                         err_perror("SETUP_MEM");
82                         return -1;
83                 }
84         }
85         *buffer_count = mem_blocks;
86
87         pi.pos_x = x;
88         pi.pos_y = y;
89         pi.out_width = w;
90         pi.out_height = h;
91         pi.enabled = enabled;
92
93         ret = ioctl(fd, OMAPFB_SETUP_PLANE, &pi);
94         if (ret != 0) {
95                 err_perror("SETUP_PLANE");
96                 err("(%d %d %d %d)\n", x, y, w, h);
97                 return -1;
98         }
99
100         ostate->pi = pi;
101         ostate->mi = mi;
102         return 0;
103 }
104
105 static int read_sysfs(const char *fname, char *buff, size_t size)
106 {
107         FILE *f;
108         int ret;
109
110         f = fopen(fname, "r");
111         if (f == NULL) {
112                 err_perror("open %s: ", fname);
113                 return -1;
114         }
115
116         ret = fread(buff, 1, size - 1, f);
117         fclose(f);
118         if (ret <= 0) {
119                 err_perror("read %s: ", fname);
120                 return -1;
121         }
122
123         buff[ret] = 0;
124         for (ret--; ret >= 0 && isspace(buff[ret]); ret--)
125                 buff[ret] = 0;
126
127         return 0;
128 }
129
130 int read_vscreeninfo(const char *fbname, int *w, int *h)
131 {
132         struct fb_var_screeninfo fbvar;
133         int ret, fd;
134
135         fd = open(fbname, O_RDWR);
136         if (fd == -1) {
137                 err_perror("open %s", fbname);
138                 return -1;
139         }
140
141         ret = ioctl(fd, FBIOGET_VSCREENINFO, &fbvar);
142         close(fd);
143
144         if (ret == -1) {
145                 err_perror("ioctl %s", fbname);
146                 return -1;
147         }
148
149         if (fbvar.xres == 0 || fbvar.yres == 0)
150                 return -1;
151
152         *w = fbvar.xres;
153         *h = fbvar.yres;
154         return 0;
155 }
156
157 int osdl_video_detect_screen(struct SDL_PrivateVideoData *pdata)
158 {
159         int fb_id, overlay_id = -1, screen_id = -1;
160         char buff[64], screen_name[64];
161         const char *fbname;
162         struct stat status;
163         int fd, i, ret;
164         int w, h;
165         FILE *f;
166
167         pdata->phys_w = pdata->phys_h = 0;
168
169         fbname = get_fb_device();
170
171         /* Figure out screen resolution, we need to know default resolution
172          * to report to SDL and for centering stuff.
173          * The only way to achieve this seems to be walking some sysfs files.. */
174         ret = stat(fbname, &status);
175         if (ret != 0) {
176                 err_perror("can't stat %s", fbname);
177                 goto skip_screen;
178         }
179         fb_id = minor(status.st_rdev);
180
181         snprintf(buff, sizeof(buff), "/sys/class/graphics/fb%d/overlays", fb_id);
182         f = fopen(buff, "r");
183         if (f == NULL) {
184                 err("can't open %s, skip screen detection", buff);
185                 goto skip_screen;
186         }
187
188         ret = fscanf(f, "%d", &overlay_id);
189         fclose(f);
190         if (ret != 1) {
191                 err("can't parse %s, skip screen detection", buff);
192                 goto skip_screen;
193         }
194
195         snprintf(buff, sizeof(buff), "/sys/devices/platform/omapdss/overlay%d/manager", overlay_id);
196         ret = read_sysfs(buff, screen_name, sizeof(screen_name));
197         if (ret < 0) {
198                 err("skip screen detection");
199                 goto skip_screen;
200         }
201
202         for (i = 0; ; i++) {
203                 snprintf(buff, sizeof(buff), "/sys/devices/platform/omapdss/display%d/name", i);
204                 ret = read_sysfs(buff, buff, sizeof(buff));
205                 if (ret < 0)
206                         break;
207
208                 if (strcmp(screen_name, buff) == 0) {
209                         screen_id = i;
210                         break;
211                 }
212         }
213
214         if (screen_id < 0) {
215                 err("could not find screen");
216                 goto skip_screen;
217         }
218
219         snprintf(buff, sizeof(buff), "/sys/devices/platform/omapdss/display%d/timings", screen_id);
220         f = fopen(buff, "r");
221         if (f == NULL) {
222                 err("can't open %s, skip screen detection", buff);
223                 goto skip_screen;
224         }
225
226         ret = fscanf(f, "%*d,%d/%*d/%*d/%*d,%d/%*d/%*d/%*d", &w, &h);
227         fclose(f);
228         if (ret != 2) {
229                 err("can't parse %s (%d), skip screen detection", buff, ret);
230                 goto skip_screen;
231         }
232
233         log("detected %dx%d '%s' (%d) screen attached to fb %d and overlay %d",
234                 w, h, screen_name, screen_id, fb_id, overlay_id);
235
236         pdata->phys_w = w;
237         pdata->phys_h = h;
238         return 0;
239
240 skip_screen:
241         /* attempt to extract this from FB then */
242         ret = read_vscreeninfo(fbname, &pdata->phys_w, &pdata->phys_h);
243         if (ret != 0 && strcmp(fbname, "/dev/fb0") != 0) {
244                 /* last resort */
245                 ret = read_vscreeninfo("/dev/fb0", &pdata->phys_w, &pdata->phys_h);
246         }
247
248         if (ret != 0) {
249                 err("VSCREENINFO has nothing meaningful");
250                 return -1;
251         }
252
253         return 0;
254 }
255
256 static int osdl_setup_omap_layer(struct SDL_PrivateVideoData *pdata,
257                 const char *fbname, int width, int height, int bpp, int *buffer_count)
258 {
259         int x = 0, y = 0, w = width, h = height; /* layer size and pos */
260         int screen_w = w, screen_h = h;
261         int tmp_w, tmp_h;
262         const char *tmp;
263         int retval = -1;
264         int ret, fd;
265
266         pdata->layer_x = pdata->layer_y = pdata->layer_w = pdata->layer_h = 0;
267
268         if (pdata->phys_w != 0)
269                 screen_w = pdata->phys_w;
270         if (pdata->phys_h != 0)
271                 screen_h = pdata->phys_h;
272
273         fd = open(fbname, O_RDWR);
274         if (fd == -1) {
275                 err_perror("open %s", fbname);
276                 return -1;
277         }
278
279         /* FIXME: assuming layer doesn't change here */
280         if (pdata->saved_layer == NULL) {
281                 struct omapfb_state *slayer;
282                 slayer = calloc(1, sizeof(*slayer));
283                 if (slayer == NULL)
284                         goto out;
285
286                 ret = ioctl(fd, OMAPFB_QUERY_PLANE, &slayer->pi_old);
287                 if (ret != 0) {
288                         err_perror("QUERY_PLANE");
289                         goto out;
290                 }
291
292                 ret = ioctl(fd, OMAPFB_QUERY_MEM, &slayer->mi_old);
293                 if (ret != 0) {
294                         err_perror("QUERY_MEM");
295                         goto out;
296                 }
297
298                 pdata->saved_layer = slayer;
299         }
300
301         tmp = getenv("SDL_OMAP_LAYER_SIZE");
302         if (tmp != NULL) {
303                 if (strcasecmp(tmp, "fullscreen") == 0)
304                         w = screen_w, h = screen_h;
305                 else if (sscanf(tmp, "%dx%d", &tmp_w, &tmp_h) == 2)
306                         w = tmp_w, h = tmp_h;
307                 else
308                         err("layer size specified incorrectly, "
309                                 "should be like 800x480");
310         }
311
312         /* the layer can't be set larger than screen */
313         tmp_w = w, tmp_h = h;
314         if (w > screen_w)
315                 w = screen_w;
316         if (h > screen_h)
317                 h = screen_h;
318         if (w != tmp_w || h != tmp_h)
319                 log("layer resized %dx%d -> %dx%d to fit screen", tmp_w, tmp_h, w, h);
320
321         x = screen_w / 2 - w / 2;
322         y = screen_h / 2 - h / 2;
323         ret = osdl_setup_omapfb(pdata->saved_layer, fd, 1, x, y, w, h,
324                                 width * height * ((bpp + 7) / 8), buffer_count);
325         if (ret == 0) {
326                 pdata->layer_x = x;
327                 pdata->layer_y = y;
328                 pdata->layer_w = w;
329                 pdata->layer_h = h;
330         }
331
332         retval = ret;
333 out:
334         close(fd);
335         return retval;
336 }
337
338 void *osdl_video_set_mode(struct SDL_PrivateVideoData *pdata,
339                           int border_l, int border_r, int border_t, int border_b,
340                           int width, int height, int bpp, int *doublebuf,
341                           const char *wm_title)
342 {
343         int buffers_try, buffers_set;
344         const char *fbname;
345         void *result;
346         int ret;
347
348         fbname = get_fb_device();
349
350         if (pdata->fbdev != NULL) {
351                 vout_fbdev_finish(pdata->fbdev);
352                 pdata->fbdev = NULL;
353         }
354
355         buffers_try = buffers_set = 1;
356         if (*doublebuf)
357                 /* actually try tripple buffering for reduced chance of tearing */
358                 buffers_try = buffers_set = 3;
359
360         ret = osdl_setup_omap_layer(pdata, fbname, width, height, bpp, &buffers_set);
361         if (ret < 0)
362                 goto fail;
363
364         pdata->fbdev = vout_fbdev_init(fbname, &width, &height, bpp, buffers_set);
365         if (pdata->fbdev == NULL)
366                 goto fail;
367
368         if (border_l | border_r | border_t | border_b) {
369                 width -= border_l + border_r;
370                 height -= border_t + border_b;
371                 result = vout_fbdev_resize(pdata->fbdev, width, height, bpp,
372                                 border_l, border_r, border_t, border_b, buffers_set);
373         }
374         else {
375                 result = osdl_video_flip(pdata);
376         }
377         if (result == NULL)
378                 goto fail;
379
380         if (!pdata->xenv_up) {
381                 int xenv_flags = XENV_CAP_KEYS | XENV_CAP_MOUSE;
382                 ret = xenv_init(&xenv_flags, wm_title);
383                 if (ret == 0) {
384                         pdata->xenv_up = 1;
385                         pdata->xenv_mouse = (xenv_flags & XENV_CAP_MOUSE) ? 1 : 0;
386                 }
387         }
388
389         if (buffers_try != buffers_set) {
390                 log("only %d/%d buffers available, expect tearing\n",
391                         buffers_set, buffers_try);
392                 *doublebuf = buffers_set > 1;
393         }
394
395         return result;
396
397 fail:
398         osdl_video_finish(pdata);
399         return NULL;
400 }
401
402 void *osdl_video_flip(struct SDL_PrivateVideoData *pdata)
403 {
404         void *ret;
405
406         if (pdata->fbdev == NULL)
407                 return NULL;
408
409         ret = vout_fbdev_flip(pdata->fbdev);
410
411         if (pdata->cfg_force_vsync)
412                 vout_fbdev_wait_vsync(pdata->fbdev);
413
414         return ret;
415 }
416
417 int osdl_video_pause(struct SDL_PrivateVideoData *pdata, int is_pause)
418 {
419         struct omapfb_state *state = pdata->saved_layer;
420         struct omapfb_plane_info pi;
421         struct omapfb_mem_info mi;
422         int enabled;
423         int fd = -1;
424         int ret;
425
426         if (pdata->fbdev != NULL)
427                 fd = vout_fbdev_get_fd(pdata->fbdev);
428         if (fd == -1) {
429                 err("bad fd %d", fd);
430                 return -1;
431         }
432         if (state == NULL) {
433                 err("missing layer state\n");
434                 return -1;
435         }
436
437         if (is_pause) {
438                 ret = vout_fbdev_save(pdata->fbdev);
439                 if (ret != 0)
440                         return ret;
441                 pi = state->pi_old;
442                 mi = state->mi_old;
443                 enabled = pi.enabled;
444         } else {
445                 pi = state->pi;
446                 mi = state->mi;
447                 enabled = 1;
448         }
449         pi.enabled = 0;
450         ret = ioctl(fd, OMAPFB_SETUP_PLANE, &pi);
451         if (ret != 0) {
452                 err_perror("SETUP_PLANE");
453                 return -1;
454         }
455
456         ret = ioctl(fd, OMAPFB_SETUP_MEM, &mi);
457         if (ret != 0)
458                 err_perror("SETUP_MEM");
459
460         if (!is_pause) {
461                 ret = vout_fbdev_restore(pdata->fbdev);
462                 if (ret != 0) {
463                         err("fbdev_restore failed\n");
464                         return ret;
465                 }
466         }
467
468         if (enabled) {
469                 pi.enabled = 1;
470                 ret = ioctl(fd, OMAPFB_SETUP_PLANE, &pi);
471                 if (ret != 0) {
472                         err_perror("SETUP_PLANE");
473                         return -1;
474                 }
475         }
476
477         return 0;
478 }
479
480 void osdl_video_finish(struct SDL_PrivateVideoData *pdata)
481 {
482         static const char *fbname;
483
484         fbname = get_fb_device();
485         if (pdata->fbdev != NULL) {
486                 vout_fbdev_finish(pdata->fbdev);
487                 pdata->fbdev = NULL;
488         }
489
490         /* restore the OMAP layer */
491         if (pdata->saved_layer != NULL) {
492                 struct omapfb_state *slayer = pdata->saved_layer;
493                 int fd;
494
495                 fd = open(fbname, O_RDWR);
496                 if (fd != -1) {
497                         int enabled = slayer->pi_old.enabled;
498
499                         /* be sure to disable while setting up */
500                         slayer->pi_old.enabled = 0;
501                         ioctl(fd, OMAPFB_SETUP_PLANE, &slayer->pi_old);
502                         ioctl(fd, OMAPFB_SETUP_MEM, &slayer->mi_old);
503                         if (enabled) {
504                                 slayer->pi_old.enabled = enabled;
505                                 ioctl(fd, OMAPFB_SETUP_PLANE, &slayer->pi_old);
506                         }
507                         close(fd);
508                 }
509                 free(slayer);
510                 pdata->saved_layer = NULL;
511         }
512
513         if (pdata->xenv_up) {
514                 xenv_finish();
515                 pdata->xenv_up = 0;
516         }
517 }
518