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