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